LoginSignup
17
6

More than 3 years have passed since last update.

Rustのマクロ中で識別子を生成する

Last updated at Posted at 2019-05-12

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で動作します。

17
6
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
6