% 可視性
Rust にはいかなる種類の vis マッチャーも存在しないため、可視性をマッチして置換するのは厄介な場合があります。
マッチして無視する
コンテキストによっては、これは繰り返しを使用して行えます。
macro_rules! struct_name { ($(pub)* struct $name:ident $($rest:tt)*) => { stringify!($name) }; } fn main() { assert_eq!(struct_name!(pub struct Jim;), "Jim"); }
上の例は、private または public な struct アイテムにマッチします。あるいは pub pub(非常に public)、さらには pub pub pub pub(本当にとてもかなり public)にもマッチします。これに対する最善の防御策は、単にそのマクロを使う人々が過度に馬鹿げたことをしないよう願うことです。
マッチして置換する
繰り返しそのものを変数に束縛することはできないため、$(pub)* の内容を、置換できるように保存する方法はありません。その結果、複数のルールが必要になります。
macro_rules! newtype_new { (struct $name:ident($t:ty);) => { newtype_new! { () struct $name($t); } }; (pub struct $name:ident($t:ty);) => { newtype_new! { (pub) struct $name($t); } }; (($($vis:tt)*) struct $name:ident($t:ty);) => { as_item! { impl $name { $($vis)* fn new(value: $t) -> Self { $name(value) } } } }; } macro_rules! as_item { ($i:item) => {$i} } #[derive(Debug, Eq, PartialEq)] struct Dummy(i32); newtype_new! { struct Dummy(i32); } fn main() { assert_eq!(Dummy::new(42), Dummy(42)); }
関連項目: AST Coercion。
この場合、グループ内の任意のトークン列にマッチできる機能を使って、() または (pub) のいずれかにマッチし、その内容を出力に置換しています。パーサーはこの位置で tt の繰り返し展開を期待しないため、展開が正しくパースされるように AST Coercion を使用する必要があります。