こちらの記事は Rustマクロ冬期講習アドベントカレンダー 5日目の記事です!
アドカレまとめ記事はこちら!: Rustマクロ作成チートシート!
ここからRustのマクロを書いていくわけですが、「マクロを利用した部分が実際にどう展開されるか」はマクロを書く際に一番気になることでしょう。
というわけで何はともあれ、 cargo-expand というツールを入れてください!
cargo install cargo-expand
プロジェクト中でマクロを使用した際にどのように展開されるか教えてくれます。
cargo expand
例:
src/main.rs
macro_rules! awesome_macro {
    () => {
        let value = "展開される部分";
        println!("{}\nネストされたマクロについても全て展開される", value);
    }
}
fn main() {
    println!("マクロが全て展開された結果がわかる");
    awesome_macro!();
}
展開結果
$ cargo expand
    Checking project v0.1.0 (/path/to/project)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.07s
#![feature(prelude_import)]
#[prelude_import]
use std::prelude::rust_2021::*;
#[macro_use]
extern crate std;
fn main() {
    {
        ::std::io::_print(
            format_args!("マクロが全て展開された結果がわかる\n"),
        );
    };
    let value = "展開される部分";
    {
        ::std::io::_print(
            format_args!(
                "{0}\nネストされたマクロについても全て展開される\n",
                value,
            ),
        );
    };
}
筆者も自信ないのですが format_args! は特殊な事情でそのままになっています。。組み込み( compiler built-in )だからかな?
大規模だったり複雑なプロジェクトでは、モジュールを指定すると対象モジュールの展開結果のみ得られて便利です!
cargo expand 対象モジュールへの::パス
例:
Rust
macro_rules! my_macro {
    () => {
        const _: &'static str = "Hello, world!";
    };
}
mod a {
    mod very {
        mod deep {
            mod module {
                my_macro!();
            }
        }
    }
}
fn main() {}
展開結果
$ cargo expand a::very::deep::module
    Checking project v0.1.0 (/path/to/project)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.07s
mod module {
    const _: &'static str = "Hello, world!";
}
まとめ
マクロを書く際に高頻度で使うことになるコマンド cargo expand を紹介しました!有効活用していきましょう!
