Rustには、「宣言的マクロ」(macro_rules!で定義する)と「手続き的マクロ」があり、「手続き的マクロ」にはさらにderiveマクロ、関数風マクロ、属性風マクロという分類がある。
手続き型マクロは、マクロの呼び出し元から入ってきたコード片を「トークンストリーム」として受け取る。PGはこのトークンストリームを処理することでマクロに与えられたコードをコンパイル時に書き換えることができる。
マクロを作る前に、トークンストリームを表示してみる。
手続き型マクロは、専用のクレートを作らなければいけない。
今回は、main関数があるほうをmymain、マクロを定義する法をmymacroとしてクレートを作成する。
Cargo.tomlに以下を記述する。
extern crate proc_macro; use proc_macro::TokenStream; #[proc_macro] pub fn print_tokens(input: TokenStream) -> TokenStream { // TokenStreamをStringに変換 let input_str = input.to_string(); // 画面に表示 println!("Received TokenStream: {}", input_str); // inputを一つ一つのTokenに分割 for tk in input.clone().into_iter() { // 画面に表示 println!("*** {}", tk); }; // 入力をそのまま返す(または他のTokenStreamを返す) input }
以下のdependenciesを追加。
[dependencies] mymacro = { path = "../mymacro" }
extern crate mymacro; fn main() { let mut value=5; mymacro::print_tokens! { value = value + 5; } print!("hello world {} ",value); }
mymainをbuildする。
すると、ビルド時に以下のようにトークンストリームが表示される(実行時ではない)。