// dictionary
マクロ.
マクロ(Macro)は、呼び出し箇所がコンパイル時に別のコードへ置き換えられる仕組みです。println!やvec!のように名前の末尾へ!を付けて呼び出すため、関数の呼び出しと見分けられます。展開された結果は普通のRustコードとしてコンパイルされるので、型チェックも通常どおり効きます。
利用側でまず目に付く違いは、引数の個数が固定でないことです。コンソール出力で使うprintln!が、渡す値の個数に関わらず同じ名前で書けるのはマクロだからです。
fn main() {
let items = vec!["りんご", "みかん"]; // 要素はいくつ並べてもよい
let prices = vec![120; 3]; // 「120を3個」という別の書き方も受け付ける
println!("{}種類", items.len()); // 引数1個
println!("{}と{}", items[0], items[1]); // 引数2個
println!("価格: {:?}", prices);
}Playgroundで開く関数との違い
| 観点 | 関数 | マクロ |
|---|---|---|
| 書き方 | foo(...) |
foo!(...) |
| 引数の個数 | 定義で固定 | 呼び出しごとに変えられる |
| 処理されるタイミング | 実行時に呼ばれる | コンパイル時に展開される |
| 受け取るもの | 値(式の評価結果) | トークン列(型やアイテムの定義なども書ける) |
| 得意なこと | 値の計算 | コードの生成(トレイト実装の自動生成など) |
値ではなくトークン列を受け取るため、ベクタ(Vec)を作るvec![120; 3]の;のように、関数の引数リストには書けない構文も使えます。
宣言的マクロと手続き的マクロ
マクロは定義の仕方で2種類に分かれます。利用側にとっての違いは、呼び出しの見た目と、依存の追加が要るかどうかです。
| 種類 | 利用側の書き方 | 例 |
|---|---|---|
| 宣言的マクロ | foo!(...) |
vec! / println! |
| 手続き的マクロ | foo!(...) / #[derive(Foo)] / #[foo] |
sqlx::query! / #[derive(Serialize)] / #[tokio::main] |
宣言的マクロはmacro_rules!で定義するもので、標準ライブラリが提供するマクロの多くはこの形式です(println!などの整形マクロは、内部でコンパイラ組み込みのformat_args!に展開されます)。
手続き的マクロは専用のクレートでしか定義できないため、利用するには外部パッケージとして依存に追加します。derive・属性の形で書くものは!が付かないので、見た目からはマクロと気付きにくい点に注意が必要です。
補足
囲む括弧はどれを使ってもよい
()・[]・{}のどれで囲んでも意味は変わりません。慣習としてvec![1, 2]は角括弧、println!("...")は丸括弧が使われます。ただし文やアイテムの位置で()・[]を使った場合は、末尾にセミコロンが必要です。
この辞書が使われているページ
backlinks 11