macro_rules!
là cả hai cleverer và dumber hơn bạn có thể nhận ra.
Ban đầu, tất cả đầu vào cho macro bắt đầu cuộc sống dưới dạng súp mã thông báo không phân biệt. An Ident
tại đây, StrLit
ở đó, vv Tuy nhiên, khi bạn kết hợp và nắm bắt một chút đầu vào, thông thường đầu vào sẽ được phân tích cú pháp trong nút Tóm tắt cú pháp cây; đây là trường hợp với expr
.
Bit "thông minh" là khi bạn thay thế ảnh chụp này (ví dụ: $expression
), bạn không chỉ thay thế mã thông báo ban đầu phù hợp: bạn thay thế toàn bộ nút AST thành một mã thông báo. Vì vậy, bây giờ có điều này không thực sự-a-token lạ trong đầu ra đó là một yếu tố toàn bộ cú pháp.
Bit "câm" là quá trình này về cơ bản không thể đảo ngược và chủ yếu là hoàn toàn vô hình. Vì vậy, chúng ta hãy lấy một ví dụ của bạn:
outer!(test);
Chúng tôi chạy này thông qua một mức độ mở rộng, và nó trở thành này:
println!("{}", inner!(test));
Trừ, đó là không những gì nó trông như thế nào. Để làm cho mọi việc rõ ràng hơn, tôi sẽ phát minh ra một số cú pháp phi tiêu chuẩn:
println!("{}", inner!($(test):expr));
Giả vờ rằng $(test):expr
là một dấu hiệu duy nhất: đó là một biểu hiện có thể được đại diện bởi các dãy thẻ test
. Đó là không phải chỉ đơn giản là trình tự mã thông báo test
. Đây là quan trọng, bởi vì khi người phiên dịch vĩ mô đi vào mở rộng mà inner!
vĩ mô, nó sẽ kiểm tra quy tắc đầu tiên:
($test:ident) => { stringify!($test) };
Vấn đề là $(test):expr
là một biểu hiện, không phải là một định danh. Có, nó chứa số nhận dạng, nhưng trình thông dịch macro không có vẻ sâu sắc. Nó thấy một biểu thức và chỉ từ bỏ.
Không khớp với quy tắc thứ hai cho cùng một lý do.
Vì vậy, bạn sẽ làm gì? ... Vâng, điều đó phụ thuộc. Nếu outer!
không làm bất kỳ loại xử lý trên đầu vào của nó, bạn có thể sử dụng một khớp tt
thay vì:
macro_rules! outer {
($($tts:tt)*) => {
println!("{}", inner!($($tts)*));
}
}
tt
sẽ phù hợp với bất kỳ cây thẻ (xem Macros chapter of the Rust Book). $($tts:tt)*
sẽ khớp với bất kỳ mã thông báo nào mà không thay đổi chúng. Đây là cách để chuyển tiếp một cách an toàn một loạt các mã thông báo sang một macro khác.
Nếu bạn cần xử lý trên dữ liệu nhập và chuyển tiếp đến macro inner!
... có thể bạn sẽ phải lặp lại quy tắc.