2015-05-17 18 views
5

Theo isuue này issueanswered question này nó không thể chỉ cần xác định một bí danh đặc điểm như:Macro để xác định đặc điểm bí danh

trait Alias = Foo + Bar; 

Cách giải quyết là một chút xấu xí:

trait Alias : Foo + Bar {} 
impl<T: Foo + Bar> Alias for T {} 

Do đó Tôi muốn xác định một macro cho việc này. Tôi cố gắng

macro_rules! trait_alias { 
    ($name : ident, $base : expr) => { 
     trait $name : $base {} 
     impl<T: $base> $name for T {} 
    }; 
} 

trait Foo {} 
trait Bar {} 

trait_alias!(Alias, Foo + Bar); 

Nhưng nó không thành công với lỗi:

src\main.rs:5:17: 5:22 error: expected one of `?`, `where`, or `{`, found `Foo + Bar` 
src\main.rs:5  trait $name : $base {} 
            ^~~~~ 

lẽ Foo + Bar không phải là một biểu hiện. Tôi đã thử một số biến thể khác nhưng không có may mắn. Có thể xác định macro như vậy không? Nó trông như thế nào?

Trả lời

8

expr là cây mã thông báo biểu hiện rõ ràng không phù hợp với vị trí bạn đã cố gắng đặt. Hãy nhớ rằng các macro Rust được nhập mạnh: chỉ các loại cây thông báo dự kiến ​​tại một vị trí nhất định mới được phép.

Bạn sẽ cần phải sử dụng trình tự lặp lại ($(…)*et al.) Của ident để đạt được điều này:

macro_rules! trait_alias { 
    ($name:ident = $base1:ident + $($base2:ident +)+) => { 
     trait $name: $base1 $(+ $base2)+ { } 
     impl<T: $base1 $(+ $base2)+> $name for T { } 
    }; 
} 

trait Foo { } 
trait Bar { } 

trait_alias!(Alias = Foo + Bar +); 

(Bạn không thể có đẹp hơn $base1:ident $(+ $base2:ident)+ hoặc $($base:ident)++ hiện nay vì lý do kỹ thuật.)

Tuy nhiên, có một kỹ thuật gian lận, làm cho trình phân tích cú pháp macro chấp nhận những thứ mà nó sẽ không khác: chuyển chúng qua macro khác và buộc nó giải thích lại các cây mã thông báo dưới dạng khác. Điều này có thể được sử dụng để có hiệu lực tốt ở đây:

macro_rules! items { 
    ($($item:item)*) => ($($item)*); 
} 

macro_rules! trait_alias { 
    ($name:ident = $($base:tt)+) => { 
     items! { 
      trait $name: $($base)+ { } 
      impl<T: $($base)+> $name for T { } 
     } 
    }; 
} 

trait Foo {} 
trait Bar {} 

trait_alias!(Alias = Foo + Bar); 

Lưu ý, tuy nhiên, nó sẽ thay đổi kiểm tra cú pháp bên trong macro, ít tối ưu.

+0

Không phải là 'ident' quá hạn chế (trong ví dụ đầu tiên)? Nó sẽ không cho phép một cái gì đó như 'other_module :: Foo'. Tôi đoán nó nên là 'con đường'. –

+0

@VladimirMatveev: vị trí giới hạn đặc điểm không giống như 'đường dẫn'. Mặc dù không sử dụng giải pháp 'items', sử dụng' ident' là lựa chọn duy nhất của bạn. –

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