マクロの定義側と利用側で識別子を共有する方法について。

基礎知識

マクロの定義側と利用側では識別子の空間が分離されている。

これにより定義側も利用側も識別子の衝突を気にせずコーディングできる。

これは衛生的 (higienic) と呼ばれる特徴である。

問題点

マクロの定義側と利用側で識別子を共有したい場合、衛生性が逆に問題となる。

例えば、マクロによるコールバックのようなパターンを考える。そこでは、マクロが定義した変数をマクロに渡したブロックが受け取り、そのブロックをマクロのコードが実行する。このような構成の場合、変数の共有は必須になる。

サンプル (失敗例)

以下では、マクロ with_var が生成した識別子 x を、マクロに与えられたブロックから参照できないためエラーになる。


macro_rules! with_var {
    ($block:block) => {
        let x = "hello";
        $block
    }
}

fn main() {
    with_var!({ println!("{x}") });
}

error[E0425]: cannot find value `x` in this scope
 --> src/main.rs:9:28
  |
9 |     with_var!({ println!("{x}") });
  |                            ^ not found in this scope

解決策

このような場合、識別子の名前をキャプチャ ($) で導入すればよい。

サンプル (成功例)

以下では、マクロ with_var が生成した識別子 x を、マクロに与えられたブロックから参照できるためエラーにならない。


macro_rules! with_var {
    ($var:ident $block:block) => {
        let $var = "hello";
        $block
    }
}

fn main() {
    with_var!(x { println!("{x}") });
}