宣言的マクロ
println!
やvec!
のように、!
を末尾につけて呼び出す処理をマクロと呼びます。
マクロは標準で用意されているものもありますが、必要に応じて自作することが可能です。マクロを定義するにはmacro_rules!
キーワードを使い、このように定義したマクロを宣言的マクロと呼びます。
宣言的マクロの構文は以下の通りです。
macro_rules! マクロ名 {
(パターン1) => {
// パターン1にマッチした場合のコード
};
(パターン2) => {
// パターン2にマッチした場合のコード
};
// 必要に応じて他のパターンを追加
}
マクロはコンパイル時に、パターンnにマッチした場合のコード
の部分がRustのソースコードに展開されます。Rustのソースコードを作るためのコードになるので、一般的なRustの文法とは異なります。
パターン
同名のマクロに、異なる引数に応じた処理を定義できます(オーバーロード)。そのために、マクロを呼び出したときに与えられた引数がどのようなものなのかをパターンで区別します。
引数のアノテーション
引数が無いマクロは以下のように定義します。
macro_rules! say_hello {
() => {
println!("Hello, World!");
};
}
fn main() {
say_hello!(); // Hello, World!が出力される
}
一つの式を受け取るマクロは以下のようにします。引数の先頭には$
を付ける必要があります。
macro_rules! double {
($x:expr) => {
$x * 2
};
}
fn main() {
let result = double!(5);
println!("5の2倍は{}", result); // 出力: 5の2倍は10
}
double!
が受け取る引数は何かしらの式である必要があるため、マクロの引数には:expr
というアノテーションを付けます。expr
は式を表します。そのほかのアノテーションには以下のようなものがあります。
block
-
ident
関数、変数の名前に使用 -
pat
(パターン)
引用:https://doc.rust-jp.rs/rust-by-example-ja/macros/designators.html
可変長引数
可変長引数は,*
で表現します。下記のコードでは、1, 2, 3, 4, 5
が($x:expr)
の繰り返しと判定され、順に0
に加算されていきます。
macro_rules! sum {
($($x:expr),*) => {
0 $(+ $x)*
};
}
fn main() {
let result = sum!(1, 2, 3, 4, 5);
println!("合計は{}", result); // 出力: 合計は15
}
関数の作成
ident
アノテーションを使うと、その引数の値を使って関数や変数を宣言することができます。下記の例では、与えた文字列と同名の関数を作成します。仕組みとしては、fn $func_name()
以降の部分がRustのソースコードとして展開されるので、直にfn foo()...
と書いているのと同じ挙動になります。
macro_rules! create_function {
($func_name:ident) => {
fn $func_name() {
println!("関数 {} が呼び出されました", stringify!($func_name));
}
};
}
// マクロを使用して関数を生成
create_function!(foo);
create_function!(bar);
fn main() {
foo(); // 出力: 関数 foo が呼び出されました
bar(); // 出力: 関数 bar が呼び出されました
}
マクロの定義はかなりローレイヤーな印象を受けます。DRYの法則を意識した機構ではありますが、使いこなすのにはそれなりの熟練度が必要そうです・・・