Rustマクロ フラグメント指定子


こちらの記事は [Rustマクロ冬期講習アドベントカレンダー](https://qiita.com/advent-calendar/2024/rust-macro) 7日目の記事です！

> アドカレまとめ記事はこちら！: [Rustマクロ作成チートシート！](https://qiita.com/namn1125/items/d601d2cbbf2cc7b5d3dc)

[前回の記事](https://qiita.com/namn1125/items/63756af0d3cecdaa02cd)で、「フラグメント指定子に慣れれば宣言マクロは制したようなもの」と言いました。

前回は以下に示すような表しか出さなかったので、今回はもう少し詳細を話していければと思います！

| フラグメント指定子 | 例 | 説明 |
|:---------------|:---|:-----|
| [`ident`](#%E8%AD%98%E5%88%A5%E5%AD%90-ident) | `hoge`, `self` | 変数名や関数名などの識別子全般 |
| [`expr`](#%E5%BC%8F-expr) | `10 + 20`, `funcall()`, `if a { b } else { c }` | 式。いわゆる評価後に値を返す何か |
| [`literal`](#%E3%83%AA%E3%83%86%E3%83%A9%E3%83%AB-literal) | `10`, `"hoge"`, `true` | リテラル。言い換えると、ソースコード内で直接表現される固有値 |
| [`stmt`](#%E6%96%87-stmt) | `let a = 10;`, `struct Hoge;`, `fn foo() {}` | ステートメント。文。何かしら意味を持つものの評価値とはならないもの |
| [`ty`](#%E5%9E%8B-ty) | `i32`, `&'a str`, `fn() -> ()` | 型 |
| [`path`](#%E3%83%91%E3%82%B9-path) | `std::option::Option`, `Turbo::<Fish>`, `::std::println` | パス。一見型と同じに見えるが、 `::` で接続されたパスを表す意味が強い。例えば `::std::thread::spawn` は関数であり型ではなく、これ自体はパスである |
| [`block`](#%E3%83%96%E3%83%AD%E3%83%83%E3%82%AF-block) | `{}` | スコープを作り出す中括弧を含めた全体 |
| [`item`](#%E3%82%A2%E3%82%A4%E3%83%86%E3%83%A0-item) | `pub fn foo() {}`, `use std::fs;`, `impl Hoge {}` | use文や関数宣言など。一部は文でもある。アイテム...という名前だとわかりにくいので、「ファイル内で最も外側に(あるいはトークン木の最上位に)配置できる要素」とおぼえておくと良さそう |
| [`pat` / `pat_param`](#%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3-pat--pat_param) | `Some(ref hoge)`, `None`, `0..10` | パターンマッチとして使用できる構造。簡単に言えば `match` 式のアーム部分になり得るもの。 `pat_param` は古いエディションとの互換性を保つためなので忘れて良い |
| [`lifetime`](#%E3%83%A9%E3%82%A4%E3%83%95%E3%82%BF%E3%82%A4%E3%83%A0-lifetime) | `'a`, `'static`, `'_` | ライフタイム |
| [`meta`](#%E3%83%A1%E3%82%BF-meta) | `derive(Debug)`, `path = "hoge"`, `serde` | 属性風マクロ等のアトリビュート(属性)、すなわち `#[]` の内側に書くことができる要素。めったに出てこないので忘れてよさそう |
| [`vis`](#%E5%8F%AF%E8%A6%96%E6%80%A7-vis) | `pub`, `pub(crate)` | Visibility、すなわち可視性の略。これのみをマッチさせる必要もないためめったに出てこない |
||||
| [`tt`](#tt-%E3%83%88%E3%83%BC%E3%82%AF%E3%83%B3%E6%9C%A8) || トークン木 (Token Tree) のことで、 **任意の** Rustコード片の塊にマッチするワイルドカード。つまりジョーカー |

:::note
「フラグメント指定子がどうしても覚えられない！！！！」という筆者のような方向けに、フラグメント指定子判別器を作ったので良かったら使ってみてください！

[Rustマクロ フラグメント指定子(ident, expr, item, stmt...)なんもわからん！となったので判別器作った #Rust - Qiita](https://qiita.com/namn1125/items/93461ddf93277b79a96f)
:::

フラグメント指定子についてどうまとめようか迷っていたのですが、下手にひねってもアレなので今回は素直に記事作成の参考にしている [The Little Book of Rust Macros](https://veykril.github.io/tlborm/decl-macros/minutiae/fragment-specifiers.html) の内容及び掲載例を拝借しつつ、紹介したい順で紹介することにします！

# `tt`: トークン木

これだけは特殊なフラグメント指定子なので最初に持ってきました。トークン木は(ほぼ) **任意の構文要素** にマッチします。いわばワイルドカードで、トランプで言えばジョーカー、ウノで言えばドロフォー、ポ◯モンで言えばメタ◯ンみたいなやつです。

「なんでトークン木って名前なの？」とか「トークン木ってなんぞ？」って方はRustマクロ冬期講習アドベントカレンダー2日目の記事 [Rustマクロの事前知識①「入出力はトークン木」 #Rust - Qiita](https://qiita.com/namn1125/items/3288db9acdf2012f78b0) を参考にしていただけると幸いです。

https://qiita.com/namn1125/items/3288db9acdf2012f78b0

活用事例としては再帰呼び出しを利用しているマクロにおいて残り部分を渡す、などです。Little Bookの例が一番わかりやすいですね。

```rust:Rust
macro_rules! mixed_rules {
    () => {};
    (trace $name:ident; $($tail:tt)*) => {
        {
            println!(concat!(stringify!($name), " = {:?}"), $name);
            mixed_rules!($($tail)*);
        }
    };
    (trace $name:ident = $init:expr; $($tail:tt)*) => {
        {
            let $name = $init;
            println!(concat!(stringify!($name), " = {:?}"), $name);
            mixed_rules!($($tail)*);
        }
    };
}

fn main() {
    let a = 42;
    let b = "Ho-dee-oh-di-oh-di-oh!";
    let c = (false, 2, 'c');
    mixed_rules!(
        trace a;
        trace b;
        trace c;
        trace b = "They took her where they put the crazies.";
        trace b;
    );
}
```

(引用元: https://veykril.github.io/tlborm/decl-macros/patterns/tt-muncher.html)

利用頻度は低いのですがとりあえず「他とは異なる」ことを強調したいため最初に持ってきました。何にでもマッチしますがおそらくフラグメント指定子として最初に出てくる候補ではありません。

# 識別子 `ident`

identifier つまり識別子 (変数名や関数名など) またはキーワードにヒットします。

```rust:Rust
macro_rules! idents {
    ($($ident:ident)*) => ();
}

idents! {
    // _ <- This is not an ident, it is a pattern
    foo
    async
    O_________O
    _____O_____
}
```

名前っぽいやつを渡したい時はだいたいこいつです！

# 式 `expr`

expression、すなわち「式」にヒットします。Rustは式指向言語なので、if "式" ( `if a { b } else { c }` ) などヒットするものが意外と多いです。

```rust:Rust
macro_rules! expressions {
    ($($expr:expr)*) => ();
}

expressions! {
    "literal"
    funcall()
    future.await
    break 'foo bar
}
```

# リテラル `literal`

文字列 `"aaa"` 、数値 `10`、 真偽値 `true` など、いわゆる言語組み込みの値にマッチします。

```rust:Rust
macro_rules! literals {
    ($($literal:literal)*) => ();
}

literals! {
    -1
    "hello world"
    2.3
    b'b'
    true
}
```

# 文 `stmt`

文、すなわちプログラムとして意味はあるものの式とは違って値を返さないとされるもの( `()` を返してるとも受け取れる...？)にヒットします。

```rust:Rust
macro_rules! statements {
    ($($stmt:stmt)*) => ($($stmt)*);
}

fn main() {
    statements! {
        struct Foo;
        fn foo() {}
        let zig = 3
        let zig = 3;
        3
        3;
        if true {} else {}
        {}
    }
}
```

今回のLittle Bookが掲載している例は興味深いのでちょっと掘り下げようと思います。

マクロが展開された結果(もとい、文としてキャプチャして文として出力し直した結果)はこうなるそうです。

```rust:Rust
/* snip */

fn main() {
    struct Foo;
    fn foo() { }
    let zig = 3;
    let zig = 3;
    ;
    3;
    3;
    ;
    if true { } else { }
    { }
}
```

セミコロンの数が何やら不自然ですね。一つ一つ見てみましょう！

- `struct Foo;` : ユニット型系構造体はセミコロンなし( `struct Foo` )では文にならないので、 `struct Foo;` まででマッチしてこれがそのまま出力される
- `fn foo() {}` : これで文になっているのでそのまま出力される
- `let zig = 3` : これで文になっているのでキャプチャされ、出力時に **セミコロンが付与されている**
- `let zig = 3;` : セミコロンより前の時点で文としてマッチして、 **セミコロンが付与される** ので結果としてここでセミコロンが増えている
- `3` : リテラルによる式は文として扱われてマッチするので、 **セミコロンが付与される** 。2回目で上記の `let zig = 3` と同様に余計なセミコロンが増えている
- `if true {} else {}`, `{}` : 同じ式でも、ブロックにより区切られるためセミコロンを必要としないものたちにはセミコロンは付与されない

# 型 `ty`

型にマッチします。 `std::option::Option` みたいな普通のものだけでなく、 `&'static dyn str` や `fn() -> ()` (関数ポインタ)みたいな型もヒットします。

```rust:Rust
macro_rules! types {
    ($($type:ty)*) => ();
}

types! {
    foo::bar
    bool
    [u8]
    impl IntoIterator<Item = u32>
}
```

# パス `path`

型と似ているけども、`use` 文などで使用する `::` でつなげられた識別子の集まりがヒットします。なぜか `Fn() -> ()` 系のやつもヒットするらしい。謎

```rust:Rust
macro_rules! paths {
    ($($path:path)*) => ();
}

paths! {
    ASimplePath
    ::A::B::C::D
    G::<eneri>::C
    FnMut(u32) -> ()
}
```

# ブロック `block`

中括弧で区切られスコープを作り、評価されると最後の式の値になる `{ }` <- こいつのことです！

```rust:Rust
macro_rules! blocks {
    ($($block:block)*) => ();
}

blocks! {
    {}
    {
        let zig;
    }
    { 2 }
}
```

# アイテム `item`

構造体や列挙体、関数、定数の定義を行う部分にヒットします。

```rust:Rust
macro_rules! items {
    ($($item:item)*) => ();
}

items! {
    struct Foo;
    enum Bar {
        Baz
    }
    impl Foo {}
    pub use crate::foo;
    /*...*/
}
```

定義類は文でもありがちなので文との違いがわかりにくいですが、アイテムは「 `fn main() {}` の外側に書くことができる構文要素」と思っておくと覚えやすいです！

# パターン `pat` / `pat_param`

パターンは `match` アームの `=>` の左側にある部分のことを指します。実は `match` 式だけではなく `let` 文や `if let` 文、関数の引数部分など、 `match` アーム以外にもパターンを活用できるシーンは多々あったりします。

```rust:Rust
macro_rules! patterns {
    ($($pat:pat)*) => ();
}

patterns! {
    "literal"
    _
    0..5
    ref mut PatternsAreNice
    0 | 1 | 2 | 3
}
```

旧エディションとの互換性のために `pat_param` というものも設けられていますが気にしなくて大丈夫です。

# ライフタイム `lifetime`

ライフタイムにヒットします。

```rust:Rust
macro_rules! lifetimes {
    ($($lifetime:lifetime)*) => ();
}

lifetimes! {
    'static
    'shiv
    '_
}
```

# メタ `meta`

一番馴染みが薄い構文要素かもしれません。属性風マクロ `#[...]` の `...` の部分がメタです。というわけで良く書くメタは例えば `derive(Debug, Clone, Copy)` とか。

```rust:Rust
macro_rules! metas {
    ($($meta:meta)*) => ();
}

metas! {
    ASimplePath
    super::man
    path = "home"
    foo(bar)
}
```

:::note
**docコメントの正体**

Little Bookの方で差し込まれていた豆知識だったのでこちらでも書くと、

```rust:Rust
struct Hoge {
    /// ドキュメントコメント
    field: u32,
}
```

みたいにつける `///` で始まるドキュメントコメントは、実は属性 `#[doc = "..."]` の糖衣構文だったりします！

だから通常のコメントと違ってコンパイラに解析されエラーになることがあるのです。
:::

# 可視性 `vis`

`pub` や `pub(crate)` といった、アイテムが他のモジュールから見えるかを指定する可視性にヒットします。

```rust:Rust
macro_rules! visibilities {
    //         ∨~~Note this comma, since we cannot repeat a `vis` fragment on its own
    //        (visの後にカンマを直接つなげることはできないので、メタ変数側に取り込んで無理やりトークンを消費する)
    ($($vis:vis,)*) => ();
}

visibilities! {
    , // no vis is fine, due to the implicit `?`
    pub,
    pub(crate),
    pub(in super),
    pub(in some_path),
}
```

本稿では説明を省略しますが、「無」にもマッチする珍しい指定子なのでマクロでもおもしろ挙動をするようです。ちなみに何も指定していない時の可視性は `pub(self)` と等しく、プライベートなアイテムになっています。

ref: https://veykril.github.io/tlborm/decl-macros/minutiae/fragment-specifiers.html#vis

# まとめ・所感

うん！やっぱり多い！フラグメント指定子を使用頻度順に解説しましたがここまで真面目に全部の解説を読んだ人なんていないでしょう。え、読んだ？ありがとうございます。

何はともあれ、慣れてきたら `ident` とか `expr` ばかりになるでしょうし、わからなくなったら[筆者が作ったツール](https://qiita.com/namn1125/items/93461ddf93277b79a96f)を使えば対応する指定子がすぐわかるので(しつこいステマ)、忘れた時に調べ直すことはあってもまともに覚えている必要はないでしょう。

https://qiita.com/namn1125/items/93461ddf93277b79a96f

このフラグメント指定子についてさえ慣れれば、宣言マクロもそしてこれからやる手続きマクロもスラスラ書けるようになります！ぜひ慣れていきましょう！