% 内部ルール
#[macro_export] macro_rules! foo { (@as_expr $e:expr) => {$e}; ($($tts:tt)*) => { foo!(@as_expr $($tts)*) }; } fn main() { assert_eq!(foo!(42), 42); }
マクロは通常のアイテムのプライバシーやルックアップと相互作用しないため、どのようなパブリックマクロも、それが依存する他のすべてのマクロを伴っていなければなりません。これは、グローバルなマクロ名前空間の汚染や、他のクレートのマクロとの競合につながる可能性があります。また、マクロを選択的にインポートしようとするユーザーを混乱させる可能性もあります。ユーザーは、公開ドキュメントに記載されていない可能性があるものも含め、すべてのマクロを推移的にインポートしなければならないためです。
優れた解決策は、本来なら別のパブリックマクロになるものを、エクスポートされるマクロの内部に隠蔽することです。上の例は、一般的な as_expr! マクロを、それを使用している公開エクスポートマクロの中へ移動する方法を示しています。
@ を使う理由は、Rust 1.2 時点では、@ トークンが前置位置で使用されていないためです。そのため、これは何とも競合しません。必要に応じて他の記号や一意の接頭辞を使ってもかまいませんが、@ の使用は広まり始めているため、これを使うことで読者がコードを理解しやすくなる可能性があります。
注:
@トークンは以前、言語がポインター型を表すために印を使用していた頃、ガベージコレクションされるポインターを表すために前置位置で使用されていました。その唯一の現在の目的は、名前をパターンに束縛することです。ただし、この用途では中置演算子として使用されるため、ここでの使用とは競合しません。
さらに、内部ルールは多くの場合、「ベア」ルールよりも前に置かれます。これは、macro_rules! が内部呼び出しを、式などのように、到底そうではあり得ないものとして誤ってパースしようとする問題を避けるためです。
少なくとも 1 つの内部マクロをエクスポートすることが避けられない場合(たとえば、共通のユーティリティルール群に依存するマクロが多数ある場合)、このパターンを使用してすべての内部マクロを単一の巨大マクロにまとめることができます。
macro_rules! crate_name_util {
(@as_expr $e:expr) => {$e};
(@as_item $i:item) => {$i};
(@count_tts) => {0usize};
// ...
}