2014-04-14 14 views
6

Về cơ bản, có hai phần cho câu hỏi này:Có thể khai báo các biến theo thủ tục sử dụng các macro Rust không?

  1. Bạn có thể vượt qua một định danh không biết đến một macro trong Rust?

  2. Bạn có thể kết hợp các chuỗi để tạo tên biến mới trong macro Rust không? !

Ví dụ, một cái gì đó như:

macro_rules! expand(
    ($x:ident) => (
    let mut x_$x = 0; 
) 
) 

Calling mở rộng (hi) rõ ràng thất bại vì hi là một định danh chưa biết; nhưng bạn có thể bằng cách nào đó làm điều này?

tức là. Tương đương trong C của một cái gì đó như:

#include <stdio.h> 
#define FN(Name, base) \ 
    int x1_##Name = 0 + base; \ 
    int x2_##Name = 2 + base; \ 
    int x3_##Name = 4 + base; \ 
    int x4_##Name = 8 + base; \ 
    int x5_##Name = 16 + base; 

int main() { 
    FN(hello, 10) 
    printf("%d %d %d %d %d\n", x1_hello, x2_hello, x3_hello, x4_hello, x5_hello); 
    return 0; 
} 

Tại sao bạn nói, ý tưởng khủng khiếp là gì. Tại sao bạn lại muốn làm điều đó?

Tôi rất vui vì bạn đã hỏi!

Xem xét khối này gỉ:

{ 
    let marker = 0; 
    let borrowed = borrow_with_block_lifetime(data, &marker); 
    unsafe { 
     perform_ffi_call(borrowed); 
    } 
} 

Bây giờ bạn có một giá trị vay với một cuộc đời một cách rõ ràng bị chặn (marker) mà không sử dụng một đời cơ cấu, nhưng chúng tôi có thể đảm bảo tồn tại cho toàn bộ phạm vi của cuộc gọi ffi; đồng thời chúng tôi không gặp lỗi mơ hồ khi một số * bị hủy tham chiếu không an toàn bên trong một khối không an toàn và do đó trình biên dịch không phát hiện lỗi đó, mặc dù lỗi được thực hiện bên trong khối an toàn.

(xem thêm Why are all my pointers pointing to the same place with to_c_str() in rust?)

Việc sử dụng một macro có thể khai báo các biến tạm thời cho mục đích này sẽ giảm bớt đáng kể những rắc rối tôi đã chiến đấu với các trình biên dịch. Đó là lý do tại sao tôi muốn làm điều này.

Trả lời

9

Có, bạn có thể vượt qua định danh tùy ý vào một macro và có, bạn có thể nối định danh vào một định danh mới sử dụng concat_idents!() vĩ mô:

#![feature(concat_idents)] 

macro_rules! test { 
    ($x:ident) => ({ 
     let z = concat_idents!(hello_, $x); 
     z(); 
    }) 
} 

fn hello_world() { } 

fn main() { 
    test!(world); 
} 

Tuy nhiên, theo như tôi biết, vì concat_idents!() chính nó là một vĩ mô , bạn không thể sử dụng số nhận dạng được ghép nối này ở mọi nơi bạn có thể sử dụng số nhận dạng đơn giản, chỉ ở những nơi nhất định như trong ví dụ ở trên và điều này theo ý kiến ​​của tôi là một trở ngại lớn. Mới hôm qua tôi đã cố gắng viết một macro có thể loại bỏ rất nhiều boilerplate trong mã của tôi, nhưng cuối cùng tôi đã không thể làm điều đó bởi vì các macro không hỗ trợ vị trí tùy ý của các mã định danh nối.

BTW, nếu tôi hiểu ý tưởng của bạn chính xác, bạn không thực sự cần ghép nối số nhận dạng để có được tên duy nhất. Các macro mộc, trái với các macro C, là hygienic. Điều này có nghĩa rằng tất cả các tên của các biến cục bộ được giới thiệu bên trong một macro sẽ không bị rò rỉ đến phạm vi mà macro này được gọi. Ví dụ, bạn có thể giả định rằng mã này sẽ làm việc:

macro_rules! test { 
    ($body:expr) => ({ let x = 10; $body }) 
} 

fn main() { 
    let y = test!(x + 10); 
    println!("{}", y); 
} 

Nghĩa là, chúng ta tạo ra một biến x và đặt một biểu sau tuyên bố của mình.Đó là sau đó tự nhiên để nghĩ rằng x trong test!(x + 10) đề cập đến rằng biến tuyên bố bởi các vĩ mô, và tất cả mọi thứ nên được tốt, nhưng trên thực tế mã này sẽ không biên dịch:

main3.rs:8:19: 8:20 error: unresolved name `x`. 
main3.rs:8  let y = test!(x + 10); 
          ^
main3.rs:3:1: 5:2 note: in expansion of test! 
main3.rs:8:13: 8:27 note: expansion site 
error: aborting due to previous error 

Vì vậy, nếu tất cả các bạn cần là tính độc đáo của người dân địa phương, sau đó bạn có thể làm gì một cách an toàn và sử dụng bất kỳ tên nào bạn muốn, chúng sẽ tự động độc đáo. Đây là explained trong hướng dẫn macro, mặc dù tôi thấy ví dụ có phần khó hiểu.

+1

concat_idents tôi có thể bị xóa, nó đã được tính năng gated [# 13295] (https://github.com/mozilla/rust/pull/13295) và [# 13294] (https://github.com/mozilla/rust/issues/13294) – Arjan

+0

Chúng tôi thực sự cần tính năng này giống như ## in C. Tôi liên tục phải viết các macro nơi người dùng đưa cho tôi hai tên vì chuỗi concat rất hạn chế. –

1

Trong trường hợp concat_idents không hoạt động (hầu hết các trường hợp tôi muốn sử dụng), việc thay đổi vấn đề từ các số nhận dạng được nối với sử dụng không gian tên sẽ hoạt động.

Đó là, thay vì mã không làm việc:

macro_rules! test { 
    ($x:ident) => ({ 
     struct concat_idents!(hello_, $x) {} 
     enum contact_idents!(hello_, $x) {} 
    }) 
} 

Người dùng có thể đặt tên không gian tên, và sau đó có những cái tên sẵn như hình dưới đây:

macro_rules! test { 
    ($x:ident) => ({ 
     mod $x { 
      struct HelloStruct {} 
      enum HelloEnum {} 
     } 
    }) 
} 

Bây giờ bạn có một tên dựa trên đối số của macro. Kỹ thuật này chỉ hữu ích trong những trường hợp cụ thể.

Các vấn đề liên quan