4 分で読了 #rust #マクロシステム
このページの目次

宣言的マクロ (macro_rules!)

macro_rules! はパターンマッチングと反復を用いてマクロを定義します——$($x:expr),* は、カンマ区切りの式がゼロ個以上あることを表します。フラグメント指定子(expr/ty/ident/tt)はマッチする構文断片のタイプを制限し、tt muncher は可変引数マクロを実装するための高度なテクニックです。宣言的マクロと関数の違い:マクロはコンパイル時に AST ノードに展開され、関数は実行時に呼び出されます——マクロは関数ではできない構文レベルの抽象化を実現できます。

macro_rules! は関数ではない

macro_rules! vec {
    ( $( $x:expr ),* ) => {                                    // マッチパターン
        {
            let mut temp_vec = Vec::new();
            $( temp_vec.push($x); )*                             // 展開パターン
            temp_vec
        }
    };
}

マクロと関数の根本的な違い:マクロはコンパイル時にコードに展開されます(実行時の呼び出しではありません)。これは、マクロが可変長引数を受け取ったり、異なるタイプのコードを生成したり、識別子を生成したりできることを意味します。これらは関数では実現できません。

フラグメント指定子:マクロの「型システム」

指定子マッチ対象
:expr任意の式42, x + y, { ... }
:ident識別子my_var, HashMap
:tyi32, Vec<String>
:tt任意のトークン木最も汎用的で、高度なマクロの構築に使用
:literalリテラル1, "hello", true
:patパターンSome(x), 1..=10
:pathパスstd::collections::HashMap
:blockブロック{ stmt1; stmt2; }
:stmtlet x = 1;, x;
:item項目fn f() {}, struct S;
:meta属性の内容derive(Debug)

反復パターン

$( $key:expr => $value:expr ),*    $(,)?       # 任意の末尾のコンマ
  • *: 0回以上
  • +: 1回以上
  • ?: 0回または1回(任意)

tt Muncher: トークン木の再帰処理

最も強力なマクロパターン——再帰と :tt を用いて任意に複雑な入力を処理します:

macro_rules! html {
    ($text:expr) => { format!("{}", $text) };
    (<$tag:ident> $($children:tt)* </$tag:ident>) => {
        format!("<{}>{}</{}>", stringify!($tag),
                html!($($children)*),            // 子要素を再帰的に展開!
                stringify!($tag))
    };
}
let s = html!(<div> "hello" <span> "world" </span> </div>);
// → "<div>hello<span>world</span></div>"

tt muncher の原理:各マクロ呼び出しで <tag> ... </tag> パターンにマッチし、その後 $children を再帰的に展開します。これは proc macro を必要とせず、純粋な macro_rules! のみで実現可能です。

参考

  • Little Book of Rust Macros: danielkeep.github.io/tlborm
  • Rust Book: 第19章 5節 — マクロ

キーワード: macro_rules!, 宣言的マクロ, 反復, tt muncher, フラグメント指定子, コンパイル時展開