一週空いて第54回です。前回はこちら。
[第54回の様子]
2022/09/28に第54回を開催した。
内容としてはRust By Example 日本語版の17. macro_rules!の「17.1. 構文」に取り組んだ。
参加者は自分を入れて3人。最近は開催頻度も低いので参加者も少なめ。久しぶりにドライバーを担当した。
教材的にはあと半年くらいは続けられそうだけど、そろそろ違う題材にしようかなあ...。
[学んだこと]
- 17.1.1. 識別子
- マクロの引数は先頭に$がつく
- マクロの引数の型には識別子designatorsが利用できる
- 以下はidentを使う例
// 関数を作成するマクロ
macro_rules! create_function {
($func_name:ident) => {
fn $func_name() {
// stringifyマクロはidentを文字列に変換
println!("You called {:?}()",
stringify!($func_name));
}
};
}
create_function!(foo); // fooという関数を作る
create_function!(bar); // barという関数を作る
fn main() {
foo(); // You called "foo"()
bar(); // You called "bar"()
}
- 以下はexprを使う例
// 式の結果を出力するマクロ
macro_rules! print_result {
($expression:expr) => {
println!("{:?} = {:?}",
stringify!($expression),
$expression);
};
}
fn main() {
print_result!(1u32 + 1); // "1u32 + 1" = 2
// ブロックも式の一種
print_result!({
let x = 1u32;
x * x + 2 * x - 1
});
// "{ let x = 1u32; x * x + 2 * x - 1 }" = 2
}
- 17.1.2. オーバーロード
- マクロルールはmacro_rules!という複数形の名前で定義することからも分かる通り、異なる引数をとってオーバーロードが可能
- matchによるパターンマッチのような感じ
macro_rules! test {
// テンプレートは自由。この場合は「test!(式1; and 式2);」のように呼び出す
($left:expr; and $right:expr) => {
println!("{:?} and {:?} is {:?}",
stringify!($left),
stringify!($right),
$left && $right)
}; // 末尾にセミコロンが必要
($left:expr; or $right:expr) => {
println!("{:?} or {:?} is {:?}",
stringify!($left),
stringify!($right),
$left || $right)
};
}
fn main() {
test!(1i32 + 1 == 2i32; and 2i32 * 2 == 4i32);
// "1i32 + 1 == 2i32" and "2i32 * 2 == 4i32" is true
test!(true; or false);
// "true" or "false" is true
}
- 折角なので、xorのパターンを実装してみる
macro_rules! test {
// andとorは省略
($left:expr; xor $right:expr) => {
println!("{:?} xor {:?} is {:?}",
stringify!($left),
stringify!($right),
$left ^ $right)
};
}
fn main() {
test!(true; xor false);
// "true" xor "false" is true
test!(true; xor true);
// "true" xor "true" is false
}
- 存在しないパターンを呼び出したらコンパイルエラーとなった(それはそう
macro_rules! test {
// and, or, xorが定義されている。省略。
}
fn main() {
// 定義されていないnandを呼び出す
test!(true; nand false);
}
// 以下のコンパイルエラー
error: no rules expected the token `nand`
--> src/main.rs:29:17
|
5 | macro_rules! test {
| ----------------- when calling this macro
...
29 | test!(true; nand false);
| ^^^^ no rules expected this token in macro call
- 17.1.3. 繰り返し
- マクロの引数は繰り返しを定義できる:可変長引数みたいな感じっぽい
- マッチ対象の引数を
$(...),+で囲むと1回以上の繰り返し - マッチ対象の引数を
$(...),*で囲むと0回以上の繰り返し
- マッチ対象の引数を
- サンプルのマクロはこんな感じ
macro_rules! find_min {
// 基本となるケース
($x:expr) => ($x);
// `$x`に少なくとも1つの`$y`が続く場合
($x:expr, $($y:expr),+) => (
// 再帰
std::cmp::min($x, find_min!($($y),+))
)
}
fn main() {
println!("{}", find_min!(1)); // 1
println!("{}", find_min!(1 + 2, 2)); // 2
println!("{}", find_min!(5, 2 * 3, 4)); // 4
}
- ちょっと難しいけど、
std::cmp::min($x, find_min!($($y),+))のfind_min!($($y),+)の部分は、引数の個数に応じて基本ケースか$(...),+のケースかが呼び出されるんだろうな、と理解した。 $(...),*を使った実装もやってみようと思ったのだけど、時間が足りず断念。なんかいい例があるかなあ...
[まとめ]
モブプログラミングスタイルでRust dojoを開催した。
これでマクロの基本構文はバッチリ、のはず...。
今週のプルリクエストはこちら。