dtolnay/pasteというcrateの紹介記事です。
通常、Rustではマクロ中で新たに識別子を生成することはできません。
fn f(n: usize) {
println!("{}", n);
}
macro_rules! call_with_n {
( $num:expr ) => {
fn call_with_$num() {
f($num)
}
};
}
call_with_n!(1); // call_with_1を定義したい
call_with_n!(2); // call_with_2を定義したい
fn main() {
call_with_1();
call_with_2();
}
これは次のようなエラーになります
error: expected one of `(` or `<`, found `1`
--> src/main.rs:7:22
|
7 | fn call_with_$num() {
| ^^^^ expected one of `(` or `<` here
...
13 | call_with_n!(1); // call_with_1を定義したい
| ---------------- in this macro invocation
ちょっとわかりにくいですが、要はcall_with_
を関数名とみなしてるわけですね。
これを可能にするのが paste
crateです
Cargo.toml
[dependencies]
paste = "*"
これで上のマクロを書き換えます
macro_rules! call_with_n {
( $num:expr ) => {
paste::item! {
fn [<call_with_ $num>]() {
f($num)
}
}
};
}
このように[< ... >]
の括弧でくくられた中の文字列(?)を結合して識別子にします。
正直どうやって動いてるか全く理解できてないのですが、これで当初の目的が達成できます。
しかもこれはconcat_idents!と違いstableで動作します。