Rust dojo第55回を開催した

1ヶ月近く空いてしまったが第55回です。前回はこちら。

kdnakt.hatenablog.com

 

 

[第55回の様子]

2022/10/26に第55回を開催した。

 

内容としてはRust By Example 日本語版の17. macro_rules!の「17.2. DRY (Don't Repeat Yourself)」〜「17.4. 可変個引数」に取り組んだ。

 

参加者は自分を入れて6人。開催頻が低い割に参加者が安定していて助かる。今回も自分がドライバーを担当した。

 

[学んだこと]

// 演算子を定義するマクロ
macro_rules! op {
    ($func:ident, $bound:ident, $method:ident) => {
        fn $func<T: $bound<T, Output=T> + Copy>(
            xs: &mut Vec<T>, ys: &Vec<T>
        ) {
            // zipは2つのイテレータを同時に回す
            for (x, y) in xs.iter_mut().zip(ys.iter()) {
                *x = $bound::$method(*x, *y);
                // *x = x.$method(*y);
            }
        }
    };
}
  • 上記のマクロを利用するとfuncで指定した名前の関数が生成される
op!(add_assign, Add, add);
op!(sub_assign, Sub, sub);

fn main() {
    let mut vec1 = Vec::new();
    vec1.push(1);
    vec1.push(2);
    let mut vec2 = Vec::new();
    vec2.push(2);
    vec2.push(3);

    add_assign(&mut vec1, &vec2);
    println!("{:?}", vec1); // [3, 5]
    println!("{:?}", vec2); // [2, 3]
}
  • マクロを使ってテストを簡略化することもできる
mod test {
    use std::iter;
    macro_rules! test {
        ($func:ident, $x:expr, $y:expr, $z:expr) => {
            #[test]
            fn $func() {
                // 配列の長さ0〜9まででテスト
                for size in 0usize..10 {
                    let mut x: Vec<_> = iter::repeat($x).take(size).collect();
                    let y: Vec<_> = iter::repeat($y).take(size).collect();
                    let z: Vec<_> = iter::repeat($z).take(size).collect();

                    super::$func(&mut x, &y);

                    assert_eq!(x, z);
                }
            }
        };
    }

    // add_assign、sub_assignをテスト
    test!(add_assign, 1u32, 2u32, 3u32);
    test!(sub_assign, 3u32, 2u32, 1u32);
}
// 計算式と計算結果を出力するマクロ
macro_rules! calculate {
    // evalはRustのキーワードではない:ここではマクロテンプレートの一部
    (eval $e:expr) => {{
        {
            let val: usize = $e; // 型を整数に制約
            println!("{} = {}", stringify!{$e}, val);
        }
    }};
}

fn main() {
    calculate! {
        eval 1 + 2
    } // 1 + 2 = 3

    calculate! {
        eval (1 + 2) * (3 / 4)
    } // (1 + 2) * (3 / 4) = 0
}
  • なんか{{ { ... } }}と三重になってるのは翻訳サイトのミスっぽい。ただ、元のサイト見ても二重になってて何か意味があるっぽかった。ただ、1個だけにしても動いたので謎。
  • 17.4. 可変個引数
  • 可変個引数のインターフェースを利用して、先ほどの計算APIを拡張するとこうなる
macro_rules! calculate {
    // 単一の`eval`のためのパターン
    (eval $e:expr) => {{
        {
            let val: usize = $e;
            println!("{} = {}", stringify!{$e}, val);
        }
    }};

    // 複数の`eval`を再帰的に分解
    (eval $e:expr, $(eval $es:expr),+) => {{
        calculate! { eval $e }
        calculate! { $(eval $es),+ }
    }};
}

fn main() {
    calculate! {
        eval 1 + 2,
        eval 3 + 4,
        eval (2 * 3) + 1
    }
}

 

[まとめ]

モブプログラミングスタイルでRust dojoを開催した。

二重かっこの謎が気になる...。

 

今週のプルリクエストはこちら。

github.com