###### tags: `Rust` `macro` `macro_rules` `macro_use` # Rust マクロ Rustのコードは再利用や抽象化のための多くのツールを持っている。 それらのユニットは豊富なsemantics構造を持つ - 関数は型シグネチャ - 型パラメータ(ジェネリクス)はトレイト境界 - オーバーロード関数はトレイト に所属する これらの構造はRustの抽象化がコンパイル時の正確性チェックを持っていることを意味する。 しかし、柔軟性の減少というコストを払っている。 # マクロ マクロ = 粒度の大きい操作をまとめて自動化する機能 コンパイラ的には、「展開された構文」の **短縮表現** Rustの抽象化では不可能なパターンのコード再利用を可能にする。 ## どういうときに使うか メリット - 構文の拡張 - 柔軟な記述 デメリット - コードの理解のしづらさ - 展開されたコードに基づくエラーが分かりづらい マクロは最終手段的な意味合いを持つ 簡潔さや適切な抽象化ができる場面もあるが、手に負えなくなる危険性とトレードオフ ## vec! マクロ ```rust= let x: Vec<u32> = vec![1, 2, 3]; // 以下のコードの短縮と見れる // Vecオブジェクトをnewして、3要素をプッシュして返す。 let x: Vec<u32> = { let mut temp_vec = Vec::new(); temp_vec.push(1); temp_vec.push(2); temp_vec.push(3); temp_vec }; ``` 以下のようなマクロで実装されている ```rust= macro_rules! vec { ( $( $x:expr ),* ) => { { let mut temp_vec = Vec::new(); $( temp_vec.push($x); )* temp_vec } }; } ``` ## マクロの構文 ### macro_rules! 新しいマクロを定義するキーワード `macro_rules! <macro名> {/*macro code*/}` vec! の ! エクスクラメーションは、マクロ呼び出しの構文の一部 マクロと関数の区別 ### マッチング 独自の構文を持つ ```rust= ($($x:expr), *) => {...}; ``` 左辺 マクロの引数に対するマッチングパターン - $x:expr : マッチャ(matcher)。任意の式にマッチ - $x : メタ変数 - expr : フラグメント指定子 - $(), * : $() の中身が 0個以上 , 感まで区切られているパターンを示している ```rust= { { let mut temp_vec = Vec::new(); $( temp_vec.push($x); )* temp_vec } }; ``` 右辺 ここで定義されたコードが生成・展開される - $ ( code( $ x )) * : 左辺でマッチした$x 変数を使って , で区切られた変数分だけコードを生成する - vec![...] に指定した要素の数だけpush()文を生成する。 ## 繰り返し $(...)* はレイヤ上で繰り返す。 左の $ x について足並みを揃えて動作する。 ## 健全なマクロシステム Cなどのマクロは、 **ソースコードを展開しているだけ** マクロより上の行で、マクロと同じ名前の変数を使っていたりすると実行結果が変わってしまう。 Rustのマクロは、外部のコードにより実行結果が変わらないようになっている。 マクロ展開は、分離された「構文コンテキスト」で行われるので、 同じ変数名が使われてもコンフリクトしない # よく使うマクロ - panic!("panic message") - vec![1,2,3] - assert!(bool) - assert_eq!(expect, real) - try!(fn()->Result<T,E>) - エラーハンドリングマクロ - Ok\<T>なら T を返す。 - Err\<E>なら return E - 戻り値がResult型の関数内で使うべし - unreacheble!(); - 実行されてはいけないコード。 - 実行されたらpanic!() - unimplemented! - 型チェック機能
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up