92416


rustcのバージョンは1.65.0です。

以下のコードはエラーが出てコンパイルできません。

**対象のコード:**

[code]

    fn main() {
        test_macro! {
            12, x, y
        }
    
        x + y + temp;
    }
    
    #[macro_export]
    macro_rules! test_macro {
        ($e:expr, $x:ident, $y:ident) => {
            let $x = $e;
            let temp = $e;
            let $y = $e;
        };
    }
    
[/code]

**エラーメッセージ:**

[code]

    error[E0425]: cannot find value `temp` in this scope
     --> src/main.rs:6:13
      |
    6 |     x + y + temp;
      |             ^^^^ not found in this scope
    
    For more information about this error, try `rustc --explain E0425`.
    error: could not compile `playground` due to previous error
    
[/code]

ところで、このコードを入力として[`cargo-expand`](https://github.com/dtolnay/cargo-
expand)を実行すると、次のようにマクロを展開したコードが得られます。

[code]

    #![feature(prelude_import)]
    #[prelude_import]
    use std::prelude::rust_2021::*;
    #[macro_use]
    extern crate std;
    fn main() {
        let x = 12;
        let temp = 12;
        let y = 12;
        x + y + temp;
    }
    
[/code]

これを読む限りは`temp`は(`x`,`y`と同じように)`x + y +
temp`の部分から見えています。(そしてこれが自分が予想していた動作でもあります)

なぜ元のコードでは`temp`が見えないのか教えてください。


CC BY-SA 4.0
2022-11-24T14:29:00.077
92412
2022-11-24T16:41:59.637
2022-11-24T16:41:59.637
3060
13199
question
rust
マクロにおける変数束縛の振る舞いについて


Rust の宣言的マクロが変数名に関し、健全（衛生的）だからです。

> 健全なマクロ（Hygienic macros）とは、識別子が誤って捕捉されてしまう問題が起こらないことが保証されているマクロである。  
>  —— [健全なマクロ -
> Wikipedia](https://ja.wikipedia.org/wiki/%E5%81%A5%E5%85%A8%E3%81%AA%E3%83%9E%E3%82%AF%E3%83%AD)

> マクロ内で導入される変数名と、マクロ呼び出し側の変数名が衝突しない。(Lispマクロの意味での衛生性)  
>  —— [Rustマクロの衛生性はどのように実現されているか(1/2)
> 識別子に関する衛生性](https://qnighy.hatenablog.com/entry/2017/03/24/070000)

質問の例ですと、マクロの`let temp = $e;` が `let temp = 12;` に展開されますが、この時 `temp`
はマクロ内に書かれていた識別子ですので、マクロの外の `temp`
とは別物となります。自動的に名前を変更してくれていると考える事もできますが、`cargo-expand` はそれを反映して視覚化してはくれないようです。

この区別はブロックにより作られるスコープに似ています。しかし、入れ子状態が視認できるブロックとは違うので、マクロで作られた部分は「別の色で塗られている」といった説明がされるようです。

> 健全マクロを理解するには、マクロが展開されるたびに、マクロから展開された部分は別の色で塗られていると考えればよい。  
>  異なる「色」の変数は、別の名前であるかのように扱われる。  
>  —— 『プログラミングRust 第2版』 21.4.4 スコープと健全マクロ

『The Little Book of Rust
Macros』でも[背景色を塗り分けることで説明](https://danielkeep.github.io/tlborm/book/mbe-min-
hygiene.html)されています。

* * *

よって、識別子をマクロの外（使用側）から持ち込めば、質問の例でもエラーになりません。

[code]

    fn main() {
        test_macro! {
            12, x, y, temp
        }
    
        println!("{}", x + y + temp);
    }
    
    #[macro_export]
    macro_rules! test_macro {
        ($e:expr, $x:ident, $y:ident, $temp_var:ident) => {
            let $x = $e;
            let $temp_var = $e;
            let $y = $e;
        };
    }
    
[/code]

あるいは、手続き的マクロでは事情が異なると思うので、より自由に識別子を扱えるかも知れません。


CC BY-SA 4.0
2022-11-24T16:39:49.920
92416
2022-11-24T16:39:49.920
3054
92412
answer