Rust勉強中 - その26 -> マクロ

# 自己紹介
出田 守と申します。
しがないPythonプログラマです。
情報セキュリティに興味があり現在勉強中です。CTFやバグバウンティなどで腕を磨いています。主に低レイヤの技術が好きで、そっちばかり目が行きがちです。

Rustを勉強していくうえで、読んで学び、手を動かし、記録し、楽しく学んでいけたらと思います。

# 環境
新しい言語を学ぶということで、普段使わないWindowsとVimという新しい開発環境で行っています。
OS: Windows10 Home 64bit 1903
CPU: Intel Core i5-3470 @ 3.20GHz
Rust: 1.39.0
RAM: 8.00GB
Editor: Vim 8.1.1
Terminal: PowerShell

# 前回
前回は並列処理について学びました。
[Rust勉強中 - その25](https://qiita.com/deta-mamoru/items/9d3fc2aaf082b626771d)

# まず1.39.0にアップデート
11/7-8にRust 1.39.0がリリースされたようです。

https://blog.rust-lang.org/2019/11/07/Rust-1.39.0.html

本題に入る前にアップデートしておきます。

```
$ rustup update stable
```
```
$ cargo --version
cargo 1.39.0 (1c6ec66d5 2019-09-30)
$ rustc --version
rustc 1.39.0 (4560ea788 2019-11-04)
$ rustdoc --version
rustdoc 1.39.0 (4560ea788 2019-11-04)
```

## 主な更新内容

* async/.awaitが安定化
* matchのガードで値が移動する場合でも共有参照が取れるようになった
* 関数やクロージャ、関数ポインタのパラメータに属性を付けられるようになった

などなど
詳しくは以下を参照:

* https://blog.rust-lang.org/2019/11/07/Rust-1.39.0.html
* https://github.com/rust-lang/rust/blob/stable/RELEASES.md#compatibility-notes
* https://tech-blog.optim.co.jp/entry/2019/11/08/080000
* https://tech-blog.optim.co.jp/entry/2019/11/08/163000

# マクロ
Rustではマクロがサポートされています。関数で表現できない処理や、短縮したい処理などをマクロとして定義して呼び出します。

## 定義
マクロの定義には`macro_rules!`を使います。

```Rust
macro_rules! macro_name {
    ...
}
```

このほかに、[組込みマクロ](https://doc.rust-lang.org/1.0.0/std/index.html#macros)や[手続きマクロ](https://doc.rust-jp.rs/the-rust-programming-language-ja/1.6/book/macros.html#%E6%89%8B%E7%B6%9A%E3%81%8D%E3%83%9E%E3%82%AF%E3%83%AD)があります。
この記事では、`macro_rules!`を使ったマクロのみに焦点を絞ります。

マクロの呼び出しは以下のように!を付けて呼び出します。

```Rust
macro_name!(...);
```

## パターンマッチ
マクロは呼び出し時に与えられた引数に対して、上から順番にパターンマッチを行います。マッチすれば定義された処理を実行します。

```Rust
macro_rules! macro_name {
    (pattern1) => { // rule1
        ...
    };
    (pattern2) => { // rule2
        ...
    };
    ...
}
```
ちなみに、パターンや処理の括弧は、適切に対応していれば`()`や`{}`、`[]`でも良いそうです。
さらに、マクロ呼び出し時も括弧が対応していれば`()`や`{}`、`[]`でも良いです。この時`{}`のみセミコロンは不要です。

## 展開
マクロの展開はコンパイルの早い段階で行われます。そして何らかのRustコードに変換されます。
vec!より良い例が浮かばなかったので、vec!を例に説明します。

```Rust
macro_rules! vec {
    ($elem:expr ; $n:expr) => { // rule1
        ::std::vec::from_elem($elem, $n)
    };
    ($($x:expr),*) => { // rule2
        <[_]>::into_vec(Box::new([$($x),*]))
    };
    ($($x:expr),+,) => { // rule3
        vec![$($x),*]
    };
}
```
\$elemや\$n, \$xはマッチした値を代入する変数のようなものです。パターン中の`:expr`はフラグメント指定子やフラグメント型と呼ばれてるみたいです(ここでは単にフラグメントと呼びます)。フラグメントは、マッチする値の種類を決めます。exprの場合はRustの式を意味します。フラグメントには以下があります。

| フラグメント | マッチ対象 | 直後のトークン |
|---|---|---|
| expr | 式 | ,; |
| stmt | 文 | ,; |
| ty | 型 | ,;=|{[:>as where |
| path | パス | ,;=|{[:>as where |
| pat | パターン |  |
| item | アイテム |  |
| block | ブロック |  |
| meta | 属性のボディ部 |  |
| ident | 識別子 |  |
| tt | トークンツリー |  |
フラグメントには、直後に来るトークンの制約を受けるものがあります。例えば、exprの場合は、直後には`,`か`;`しか来ません。
identとttに関してはRustの構文に属さない値にマッチさせることができます。
identは任意の識別子に、ttは適切に対応した括弧に囲まれている部分あるいは、括弧に囲まれていない単一のトークンにマッチします。

vec!は以下の3パターンで呼び出すことができます。

```Rust
vec![0; 3];     // rule1
vec![0, 1, 2];  // rule2
vec![0, 1, 2,]; // rule3
```
まず、`vec![0; 3];`の引数は`0; 3`となり、rule1に当てはめます。rule1のパターンは`$elem:expr ; $n:expr`です。まず`0`は$elem:exprにマッチします。次に`;`はそのまま`;`にマッチします。最後に`3`も`$n:expr`にマッチします。結果、rule1にマッチすることになり、その処理を実行します。
パターンマッチ時はコメントやホワイトスペースは無視されます。

## 繰り返し
次に、`vec![0, 1, 2]`のパターンマッチをみていきます。この場合引数は`0, 1, 2`になります。最初にrule1に当てはめますがマッチしません。次にrule2に当てはめてみます。rule2のパターンは`$($x:expr),*`のようになっています。これの意味は、「$x:exprのカンマ区切りで0回以上の繰り返し」という意味です。このとき、**繰り返しの対象は$($$x:expr)となります。**正規表現のようにカンマの繰り返し出ないことに注意です。繰り返しの表現は`*`と`+`があります。

| パターン | 意味 |
|---|---|
| $(...)* | 0個以上にマッチ|
| $(...)+ | 1個以上にマッチ|

rule2のパターンマッチに戻ります。`0, 1, 2`はカンマをセパレータに繰り返されているので、マッチします。結果、rule2の処理を実行します。ちなみに、処理中でも繰り返しが使われています。

## 再帰
最後に`vec![0, 1, 2,]`のパターンマッチをみていきます。引数は`0, 1, 2,`となり、最後にカンマがきた形になっています。これは、rule1にマッチしません。次のrule2にもマッチしません。最後にカンマがこないからです。
最後のrule3に当てはめてみます。rule3のパターンは`$($x:expr),+,`です。意味は「$x:exprのカンマ区切りで1回以上の繰り返しで最後にカンマ」ですね。これは、引数とマッチしますので、rule3の処理が実行されます。
rule3の処理は`vec![$($x),*]`となっており最後のカンマを抜いて、自分自身を呼び出しています。つまり、マクロを再帰的に呼び出していることになります。

## デバッグ
マクロはコンパイル時にRustコードに展開されます。展開後のコードを確認するには、以下の手順で行います。

1. Rustをnightlyにします。`rustup default nightly`
2. `cargo run --verbose`でrustcのコマンドを確認。
3. rustcコマンドのオプションに`-Z unstable-options --pretty expanded`を追加して実行

vec!の展開結果は以下のようになりました。分かりやすいように改行やインデントを入れています。

```Rust
macro_rules! vec {
    ($ elem : expr ; $ n : expr) =>
        { :: std :: vec :: from_elem ($ elem, $ n) } ; 
    ($ ($ x : expr), *) =>
        { < [_] > :: into_vec (Box :: new ([$ ($ x), *])) } ; 
    ($ ($ x : expr), +,) => { vec ! [$ ($ x), *] } ;
}
```
また、`log_syntax!`を使う方法や、`trace_macros!(true)`を使う方法もあります。

## インポートとエクスポート
単一クレートでは、

* あるモジュールで見えているマクロは、自動的にその子モジュールからもそのマクロが見える
* あるモジュールから親モジュールへエクスポートするには`#[macro_use]`を使う
    
複数クレートでは、

* ほかのクレートからマクロをインポートするには、extern crate宣言に`#[macro_use]`を付ける
* クレート内のマクロをエクスポートするには、マクロそれぞれに`#[macro_export]`を付ける


今回はここまで。