Rustで書く！ 世界一分かりやすいマクロ入門

# はじめに
本記事はRustの`proc_macro`を用いて、自由自在にマクロを操れるようになろうというものです。つい1ヶ月前まで私も「マクロ？なにそれ美味しいの？」状態だったので全く難しいものでもありません。この記事を通してマクロを書けるようになると皆様のRustコード全体の見通しが良くなり、眩しいプログラミングライフを手に入れられることでしょう！！

# 読者対象
* Rustに興味があるけどなかなか手を出せてない方
* ある程度の手続き的なプログラミングの経験がある方
* そもそもマクロってなんなのか分からない方
* etc...

# そもそもマクロとは

簡単にいうと「**プログラムを生成するプログラム**」です。
「？？？」となる方も少なくないと思います。簡単な例を見てみましょう。

---

新入社員のヒヨコのピヨ子さん(🐣)は上司(🐔)から10000個の要素を持つEnumを明日までに定義してねと頼まれました。
```rust: 10000個のバカデカEnum.rs
enum Sample {
    Value1,
    Value2,
    ...
}
```
5時間経ちましたがピヨ子さんは300個を超えたあたりで腱鞘炎になり退勤...

ピヨ子さんはどうすればよかったのでしょうか。1から10000までのEnumを手作業で生成していたら日が暮れてしまうのは明白でしょう。
そこで出てくるのが「**マクロ**」です！！

```rust: 10000個のバカデカEnum.rs
bakadeka_10000_enum!();

fn main() {
    let value1 = Sample::Value1;
    let value9567 = Sample::Value9567;
}
```
雰囲気だけ掴んで頂きたいのでここでは内部実装を割愛しますが、こんな感じにEnumを定義できちゃいます。
「バカデカEnumなんて需要ねぇんだよ！」と思ってる方もいらっしゃるかもしれませんが、マクロでできる0.01%を紹介したまでです。怒らないでください。

# 事前学習
### 注意
`proc_macro`はライブラリクレート内でのみ定義可能です。
それ以外の場所で定義するとコンパイラに怒られるので新しくプロジェクトファイルを作成して下さい。

### 必要になる`crate`と設定
`proc_macro`を定義するクレートの`cargo.toml`では`proc-macro = true`とする必要があります。
また構文木を扱いやすくするための便利なクレート(`syn`, `proc-macro2`, `quote`)があるので`dependencies`に追加して下さい。
`anyhow`は好みで構いません。

```toml: cargo.toml
[package]
name = "macros"
version = "0.1.0"
edition = "2021"
resolver = "2"

[dependencies]
proc-macro2 = "1.0.69"
syn = "2.0.38"
quote = "1.0.33"
anyhow = "1.0.75"

[lib]
proc-macro = true
```

# 実際になにか作ってみよう
百聞は一見にしかず。作りながら一緒に覚えていきましょう。

### 作るもの
競技プログラミングの標準入力を簡単にできるようにするマクロを作ってみましょう。
次の様な入力があったとします。
```shell: 入力
hello
1
1 2 3 4 5
1 2 3
4 5 6
7 8 9
1 Hello
2 Hi
1 Hello 2 3
4 Wow 5 6
1 2
```
普通に書くのであれば`for`文をぶん回したりして面倒臭いですが、次のように受け取れたら楽ですよね。
```rust: main.rs
fn main() {
    include_input! {
        hello: String,
        one: i128,
        array: [u8; 5],
        array_array: [[i32; 3]; 3],
        array_tuple1: [(u16, String); 2],
        array_tuple2: [(u16, String, usize, usize); 2],
        tuple: (u8, u8),
    };

    println!(
        "\n{:?}\n{:?}\n{:?}\n{:?}\n{:?}\n{:?}\n{:?}",
        hello, one, array, array_array, array_tuple1, array_tuple2, tuple
    );
}
```
今回はこれを作ってみましょう。

### 入力の受け取り
```rust
include_input! {
    hello: String,
    one: i128,
    array: [u8; 5],
    array_array: [[i32; 3]; 3],
    array_tuple1: [(u16, String); 2],
    array_tuple2: [(u16, String, usize, usize); 2],
    tuple: (u8, u8),
};
```
`フィールド名: 型`と`,`の形が連続していることが分かります。まずはこれを受け取れるようにしましょう。
「面倒な書き方で定義するのかな？」って思ったでしょ。めっちゃ簡単に定義できます。

まず`フィールド名: 型`から定義してみます。
```rust: name_and_type.rs
use proc_macro2::Ident;
use syn::parse::{Parse, ParseStream};
use syn::{Token, Type};

pub struct NameAndType {
    name: Ident,
    _colon: Token![:],
    ty: Type,
}

impl Parse for NameAndType {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        let name = input.parse()?;
        let _colon = input.parse()?;
        let ty = input.parse()?;
        Ok(NameAndType { name, _colon, ty })
    }
}
```
あれ、めちゃめちゃ簡単ですね。
* `name: Ident`
    * ここはフィールド名など一意なデータを指すものにマッチします。`Ident`は他にも変数名や構造体名などにもマッチするので`derive`マクロを書く時にもお世話になります。
* `_colon: Token![:]`
    * ここは見ての通り`:`にマッチします。実際には`_colon`を使ってなにかをするわけではないのですが、マッチ条件の可読性を上げるために公式が書くことを推奨しています。意地でも書きたくなければスキップしても良いでしょう。
* `ty: Type`
    * ここは型名にマッチします。`u8`, `String`, `[u16; 5]`など、型であればマッチします。


これで入力の第1ステップは完了です。
続いて`NameAndType`と`,`の繰り返しにマッチするようにしましょう。
繰り返しに関しては`syn::punctuated::Punctuated<T, P>`が役に立ちます。

```rust: input_punctuated.rs
use crate::name_and_type::NameAndType;
use std::ops::Deref;
use syn::parse::{Parse, ParseStream};
use syn::punctuated::Punctuated;
use syn::Token;

pub struct MyPunctuated(Punctuated<NameAndType, Token![,]>);

impl Parse for MyPunctuated {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        Ok(MyPunctuated(Punctuated::parse_terminated(input)?))
    }
}

impl Deref for MyPunctuated {
    type Target = Punctuated<NameAndType, Token![,]>;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}
```
このように簡単に定義できてしまいました。 `Deref`はお好みで追加して下さい。
あとはマクロ本体を定義するだけで入力までは動くようになります。

```rust: lib.rs
use input_punctuated::MyPunctuated;
use proc_macro::TokenStream;
use syn::parse_macro_input;

#[proc_macro]
pub fn include_input(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as MyPunctuated);
    TokenStream::new()
}
```
マクロを定義するときは`#[proc_macro]`と関数の頭に書くだけで`include_input!(...)`のように呼び出すことが可能になります。
引数と戻り値に`TokenStream`とありますがこれは構文木と言って、プログラムを木構造として保持しているものになります。
`input`を先ほど定義した`MyPunctuated`に変換して、戻り値は一旦空の`TokenStream`を返していざ実行してみましょう！
```rust: main.rs
fn main() {
    include_input! {
        hello: String,
        one: i128,
        array: [u8; 5],
        array_array: [[i32; 3]; 3],
        array_tuple1: [(u16, String); 2],
        array_tuple2: [(u16, String, usize, usize); 2],
        tuple: (u8, u8)
    }
}
```
おそらく何も起こらずに終了したかと思います。しかし内部では全ての`フィールド名: 型,`はしっかり`MyPunctuated`に変換されています。
これで晴れて入力は完全にマスターしましたね！

### プログラムを自動生成してみる
```rust: expand_input.rs
use proc_macro2::TokenStream;
use quote::quote;

pub fn expand_input(input: MyPunctuated) -> TokenStream {
    quote! {
        println!("Hello, World!");
    }
}
```
新しくファイルを作って`MyPunctuated`から`TokenStream`を生成する関数を定義しましょう。
ここで注意が必要なのが、ここで使う`TokenStream`は`proc_macro2::TokenStream`です。`lib.rs`の`TokenStream`は`proc_macro::TokenStream`なので気をつけましょう。

では`lib.rs`に`expand_input`をimportして書き直してみましょう。
```rust: lib.rs
use crate::input::expand_input;
use input_punctuated::MyPunctuated;
use proc_macro::TokenStream;
use syn::parse_macro_input;

#[proc_macro]
pub fn include_input(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as MyPunctuated);
    expand_input(input).into()
}
```
`proc_macro2::TokenStream`から`proc_macro::TokenStream`に変換するには、Into traitが実装されているので末尾に`.into()`を書くだけで良いです。

実際に動かしてみてください！なにもないところから`Hello, World!`が現れたと思います。これは何を意味しているかというと、入力の変換に成功して尚且つプログラムの自動生成に成功したことを意味しています。

### `MyPunctuated`からプログラムを生成する
`MyPunctuated`は`,`で区切られた`NameAndType`をもとに生成しているので直感的に`MyPunctuated`はiteratorだと勘付くかもしれません。
実はその通りで、`Deref`を実装していれば`MyPunctuated`から`iter()`を呼べて、個々の`NameAndType`にアクセスできます。これをもとにプログラムを自動生成するのです。

```rust: input.rs
pub fn expand_input(input: MyPunctuated) -> anyhow::Result<TokenStream> {
    let token_streams = input
        .iter()
        .map(|field| (field.name(), field.ty()))
        .map(|(ident, ty)| expand_several_type(ident, ty, 0))
        .collect::<Vec<_>>();
    // Vec<Option<TokenStream>> => Vec<TokenStream> に変換する
    let mut result = Vec::new();
    for token_stream_result in token_streams {
        result.push(token_stream_result?);
    }
    Ok(quote! {
        #(#result)*
    })
}

fn expand_several_type(ident: &Ident, ty: &Type) -> anyhow::Result<TokenStream> {
    todo!()
}
```
`Ident`と`Type`からプログラムを生成できるように`expand_several_type(ident: &Ident, ty: &Type)`を定義しましょう！`expand_several_type`は入力に依存する部分なので`anyhow`を用いて失敗可能性を表現しておきましょう。
(panicでも別に良いですが、Resultからcompile_errorに変換することが可能なのでpanicで逃げる必要性は薄いと言って良いと思ってます)

ここで以下のような表現にびっくりしているかもしれません。
```rust
quote! {
    #(#result)*
}
```
実はquoteマクロの中では`#`を用いて変数を展開することが可能なのです！
(厳密には`ToTokens` traitを実装している型であれば可能)

文字列型や数字型はもちろんのことながら、`Vec<TokenStream>`にも対応していて表現の幅が広いです。
`Ident`も展開可能なので`Ident`を生成してやれば任意の文字を展開できます。
`Vec<TokenStream>`に対しては`#(#token_streams),*`のようにすると`?,?,?,...`のように展開させることが可能です。

```rust: example.rs
let number = 1;
let string = String::from("hello");
let ident = Ident::new(&format!("{}_{}", string, number), Span::call_site());
let token_streams = vec![quote!(10), quote!("Wow"), quote!("Yay")];
quote! {
    let hoge = #number; // let hoge = 1;       として展開される
    let fuga = #string; // let fuga = "hello"; として展開される
    let #ident = 1u8;   // let hello_1 = 1u8;  として展開される
    let tuple = (#(#token_streams),*); // let tuple = (10, "Wow", "Yay"); として展開される
}
```
コレが分かれば、あとはもうパズルのように組み立てるだけですね！
実際に組み立ててみたのが以下の通りです。

```rust:input.rs
/// Typeに合わせて展開する
fn expand_several_type(ident: &Ident, ty: &Type, depth: i8) -> anyhow::Result<TokenStream> {
    // サポートするのは2次元までにする
    if depth >= 3 {
        bail!("Maximum depth reached");
    }
    match ty {
        Type::Array(type_array) => expand_array(ident, type_array.clone(), depth),
        Type::Tuple(type_tuple) => Ok(expand_tuple(ident, type_tuple.clone(), depth)),
        Type::Path(_) => Ok(quote! {
            let mut #ident = String::new();
            ::std::io::stdin().read_line(&mut #ident).expect("failed to read");
            let #ident = #ident.trim().to_string().parse::<#ty>().unwrap();
        }),
        _ => bail!("Unsupported type"),
    }
}

/// タプルとして展開する
fn expand_tuple(ident: &Ident, type_tuple: TypeTuple, depth: i8) -> TokenStream {
    let token_streams = type_tuple
        .elems
        .iter()
        .enumerate()
        .map(|(i, ty)| {
            quote! {
                split_input[#i].parse::<#ty>().unwrap()
            }
        })
        .collect::<Vec<_>>();
    let token_stream = quote! {
        let mut input = String::new();
        ::std::io::stdin().read_line(&mut input).expect("failed to read");
        let trimed_string = input.trim().to_string();
        let split_input = trimed_string.split(' ').collect::<Vec<_>>();
    };
    if depth == 1 {
        quote! {
            #token_stream
            let #ident = (#(#token_streams),*);
        }
    } else {
        quote! {
            #token_stream
            #ident.push((#(#token_streams),*));
        }
    }
}

/// 配列として展開する
fn expand_array(ident: &Ident, type_array: TypeArray, depth: i8) -> anyhow::Result<TokenStream> {
    let (array_element_type, array_length) = (type_array.elem, type_array.len);
    match *array_element_type {
        Type::Array(_) | Type::Tuple(_) => {
            let token_stream = expand_several_type(ident, &array_element_type, depth + 1)?;
            Ok(quote! {
                let mut #ident = Vec::new();
                for _ in 0..#array_length {
                    #token_stream
                }
                let #ident: [#array_element_type; #array_length] = #ident.try_into().expect("Failed to cast to an array from Vec");
            })
        }
        Type::Path(_) => {
            let token_stream = quote! {
                .trim()
                .split(" ")
                .map(|n| n.parse::<#array_element_type>().expect("Failed to cast"))
                .collect::<Vec<_>>()
                .try_into()
                .expect("Failed to cast to an array from Vec");
            };
            let result = if depth == 1 {
                quote! {
                    let mut #ident = String::new();
                    ::std::io::stdin().read_line(&mut #ident).expect("Failed to read");
                    let #ident: [#array_element_type; #array_length] = #ident #token_stream
                }
            } else {
                quote! {
                    let mut input = String::new();
                    ::std::io::stdin().read_line(&mut input).expect("Failed to read");
                    let input: [#array_element_type; #array_length] = input #token_stream
                    #ident.push(input);
                }
            };
            Ok(result)
        }
        _ => {
            bail!("Unsupported type");
        }
    }
}
```
複雑に見えますが、ゴールを想像するところから始めればとても簡単です。
おそらく最初は知らない型が多くあると思いますが、`tuple`や`struct`が何を持っているか(識別子・Visility・型など)を頭の片隅に置いておくと大体何を指しているのか分かるはずです。

仕上げは`Result`型にしたので`Err`の場合はコンパイルエラーにしてあげましょう。

```rust:lib.rs
#[proc_macro]
pub fn include_input(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as MyPunctuated);
    expand_input(input)
        .map_err(|e| syn::Error::new(proc_macro2::Span::call_site(), e.to_string()))
        .unwrap_or_else(|e| e.into_compile_error())
        .into()
}
```
`map_err`を用いて`syn`の`Error`に変換してあげるだけでOKです。
完成しましたね！ お疲れ様でした。

# 実行してみよう🎉
### 入力
```terminal
-> cargo run
hello
1
1 2 3 4 5
1 2 3
4 5 6
7 8 9
1 Hello
2 Hi
1 Hello 2 3
4 Wow 5 6
1 2
```

### 実行結果
```terminal
"hello"
1
[1, 2, 3, 4, 5]
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
[(1, "Hello"), (2, "Hi")]
[(1, "Hello", 2, 3), (4, "Wow", 5, 6)]
(1, 2)
```

しっかり型変換もできていますね！
タプルや配列もしっかり生成されています。

# まとめ
`quote`マクロを使えば簡単にさまざま表現ができることがわかりました。
マクロと聞いて抵抗感があった人でも少しは心理的ハードルが下がったのではないでしょうか。
まだまだ他にも紹介しきれていないものもありますが、今回は入門記事ということでここらへんでやめておこうと思います。