こちらの記事は Rustマクロ冬期講習アドベントカレンダー 9日目の記事です!
今回から数回にわたり、重要ではないけど知っておくとちょっとお得かもしれない宣言マクロの小ネタを軽く紹介したいと思います。
以下、今回の出典元です。
Debugging - The Little Book of Rust Macros
超有能文字列化マクロ stringify
ここまでの記事で何度か登場していますが、与えられたトークン木をそのまま ""
で囲まれた文字列にしてくれる stringify
というマクロがあります。
macro_rules! impl_hello {
($name:ty) => {
impl $name {
fn hello(&self) {
println!("Hello. My name is {}!", stringify!($name));
}
}
};
}
fn main() {
struct Hoge;
impl_hello(Hoge);
Hoge.hello();
}
Playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=9a3768aa8341aea9143d9da298d91427
...便利ですね!特にこれ以上解説することはないのですが、宣言マクロだけではなく手続きマクロでもかなりの頻度で使うマクロなのでここで紹介させていただきました。特に例えば上記で示したような、構造体名などの ident
をプログラムで出力したいといった時に頻出です。
マッチした中身を確かめたい
生成されるマクロをデバッグしたい祭、 cargo expand
による方法があることを紹介しました。
Rustマクロの事前知識④ 「聴診器 cargo-expand」 #Rust - Qiita
それ以外にも2つ、コンパイル時にマッチした中身を確認するマクロがあるので、ここで紹介したいと思います。
まぁどちらも nightly (実験機能) なのですが
trace_macros
マクロが「どのように」展開されていくかを教えてくれるマクロです。
真偽値を渡して有効化する仕組みになっており、 trace_macros!(true);
から trace_macros!(false);
で囲まれた範囲の中にあるマクロが展開されていく様子をコンパイル時にログ出力してくれます!
#![feature(trace_macros)]
macro_rules! each_tt {
() => {};
($_tt:tt $($rest:tt)*) => {each_tt!($($rest)*);};
}
fn main() {
each_tt!(foo bar baz quux);
trace_macros!(true); // デバッグON!
each_tt!(spim wak plee whum);
trace_macros!(false); // デバッグOFF!
each_tt!(trom qlip winp xod);
}
$ cargo +nightly build
Compiling project v0.1.0 (/path/to/project)
note: trace_macro
--> src/main.rs:12:5
|
12 | each_tt!(spim wak plee whum);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: expanding `each_tt! { spim wak plee whum }`
= note: to `each_tt! (wak plee whum);`
= note: expanding `each_tt! { wak plee whum }`
= note: to `each_tt! (plee whum);`
= note: expanding `each_tt! { plee whum }`
= note: to `each_tt! (whum);`
= note: expanding `each_tt! { whum }`
= note: to `each_tt! ();`
= note: expanding `each_tt! { }`
= note: to ``
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.49s
Playground: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=ef2a3033512b33bda9489ab15582c1cf
マクロの再帰的な展開もトレースしてくれるので、前回見せたような再帰を利用したマクロがどう展開されていくかをデバッグするのに便利です!
#![feature(trace_macros)]
macro_rules! list {
( $v:expr ) => {
Rc::new(Cons($v, Rc::new( Nil )))
};
( $v:expr, $($rest:expr),* ) => {
Rc::new(Cons($v, list!($($rest),*) ))
};
}
#[allow(dead_code)]
#[derive(Debug)]
enum List {
Cons(i32, Rc<List>),
Nil,
}
use std::rc::Rc;
use List::{Cons, Nil};
fn main() {
trace_macros!(true);
let _ = list![1, 2, 3, 4, 5];
trace_macros!(false);
}
$ cargo +nightly build
Compiling project v0.1.0 (/path/to/project)
note: trace_macro
--> src/main.rs:24:13
|
24 | let _ = list![1, 2, 3, 4, 5];
| ^^^^^^^^^^^^^^^^^^^^
|
= note: expanding `list! { 1, 2, 3, 4, 5 }`
= note: to `Rc :: new(Cons(1, list! (2, 3, 4, 5)))`
= note: expanding `list! { 2, 3, 4, 5 }`
= note: to `Rc :: new(Cons(2, list! (3, 4, 5)))`
= note: expanding `list! { 3, 4, 5 }`
= note: to `Rc :: new(Cons(3, list! (4, 5)))`
= note: expanding `list! { 4, 5 }`
= note: to `Rc :: new(Cons(4, list! (5)))`
= note: expanding `list! { 5 }`
= note: to `Rc :: new(Cons(5, Rc :: new(Nil)))`
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.11s
Playground: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=e8f0f28bb12f3995a9613c8f4d1b9783
log_syntax
受け取ったメタ変数(というかトークン木...?)をコンパイル時にそのままログ出力してくれるマクロです!
#![feature(log_syntax)]
macro_rules! list {
( $v:expr ) => {
{
log_syntax!($v);
Rc::new(Cons($v, Rc::new( Nil )))
}
};
( $v:expr, $($rest:expr),* ) => {
{
log_syntax!($v);
Rc::new(Cons($v, list!($($rest),*) ))
}
};
}
#[allow(dead_code)]
#[derive(Debug)]
enum List {
Cons(i32, Rc<List>),
Nil,
}
use std::rc::Rc;
use List::{Cons, Nil};
fn main() {
let _ = list![1, 2, 3, 4, 5];
}
$ cargo +nightly build
Compiling project v0.1.0 (/path/to/project)
1
2
3
4
5
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.12s
Playground: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=da9cbfe2fcd0390e9e70ee23d29b1584
正直使い所わからんかも...局所的に確認したい時は trace_macros
より便利かもしれないですね
まとめ
マクロを書く時に便利そうなマクロを紹介しました!
cargo expand
と併用して使用するとマクロが幾分か楽に書けるようになるかもしれません、多分...!