Rust dojo第7回を開催した

第7回です。

前回はこちら。

kdnakt.hatenablog.com

 

 

[第7回の様子]

2021/08/25に第7回を開催した。

今回はちょっと遅れて、無事参加できた。5分遅刻した結果先に始まっていたけど、みんな自律的に動けていて良い。次は遅刻しないようにしよう(そもそも直前にMTGを入れなければよいのだが...)。

 

内容としてはRust By Example 日本語版の「2.2. タプル」を読んで手を動かした。

 

参加者は全部で10人。ひさびさの2桁!なかなか盛況である。

今回も、前回終了時に決めておいたメンバーがドライバーを担当。

 

今週も開催前後のSlackでのやりとりがたくさん🎉

f:id:kidani_a:20210827125228p:plain

 

タプルの制約の話。

f:id:kidani_a:20210827125419p:plain

 

所有権難しい...。

f:id:kidani_a:20210827125321p:plain

 

手を動かしててエライ。

f:id:kidani_a:20210827125517p:plain

 

来週はぜひぜひ。

f:id:kidani_a:20210827125607p:plain

 

[学んだこと]

let tuple = (10, true);
  • 関数が複数の値を返すときに使うらしい
  • 様々な型をまとめられる:まとめた値にはインデックスでアクセスできる
let long_tuple = (1u8, 2u16, 3u32, 4u64,
              -1i8, -2i16, -3i32, -4i64,
              0.1f32, 0.2f64,
              'a', true);

println!("long tuple first value: {}", long_tuple.0);
println!("long tuple second value: {}", long_tuple.1);
  • タプルのタプルも定義できる
let tuple_of_tuples = ((1u8, 2u16, 2u32), (4u64, -1i8), -2i16);
  • タプルの要素は12個までならprintlnで出力できる:13個だとコンパイルエラーになる(printlnしなければ利用可能)
let too_long_tuple = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13);
println!("too long tuple: {:?}", too_long_tuple.4);

// 上記コードは以下のコンパイルエラー
error[E0277]: `({integer}, {integer}, {integer}, {integer}, {integer}, {integer}, {integer}, {integer}, {integer}, {integer}, {integer}, {integer}, {integer})` doesn't implement `Debug`
  --> src/main.rs:39:38
   |
39 |     println!("too long tuple: {:?}", too_long_tuple);
   |                                      ^^^^^^^^^^^^^^ `({integer}, {integer}, {integer}, {integer}, {integer}, {integer}, {integer}, {integer}, {integer}, {integer}, {integer}, {integer}, {integer})` cannot be formatted using `{:?}` because it doesn't implement `Debug`
   |
   = help: the trait `Debug` is not implemented for `({integer}, {integer}, {integer}, {integer}, {integer}, {integer}, {integer}, {integer}, {integer}, {integer}, {integer}, {integer}, {integer})`
   = note: required by `std::fmt::Debug::fmt`
   = note: this error originates in the macro `$crate::format_args_nl` (in Nightly builds, run with -Z macro-backtrace for more info)
  • タプルは関数の引数や戻り値に利用できる
fn reverse(pair: (i32, bool)) -> (bool, i32) {
    // 分解して別の変数に代入できる
    let (integer, boolean) = pair;
    // 別のタプルを生成する
    (boolean, integer)
}

let pair = (1, true);
println!("pair is {:?}", pair);
// pair is (1, true)

println!("the reversed pair is {:?}", reverse(pair));
// the reversed pair is (true, 1)
  • 1要素だけのタプルをつくる場合にはカンマを忘れずに
println!("one element tuple: {:?}", (5u32,));
// one element tuple: (5,)

println!("just an integer: {:?}", (5u32));
// just an integer: 5
  • 演習1:Matrix構造体にいい感じのfmt::Displayトレイトを実装せよ
#[derive(Debug)]
struct Matrix(f32, f32, f32, f32);

impl fmt::Display for Matrix {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "( {} {} )\n( {} {} )", self.0, self.1, self.2, self.3)
    }
}


let matrix = Matrix(1.1, 1.2, 2.1, 2.2);
println!("{:}", matrix);
// ( 1.1 1.2 )
// ( 2.1 2.2 )
  • 演習2:Matrix構造体の要素を入れ替えるtranspose()関数を実装せよ
  • 最初の実装はこんなかんじ
#[derive(Debug)]
struct Matrix(f32, f32, f32, f32);

fn transpose(matrix: Matrix) -> Matrix {
    Matrix(matrix.0, matrix.2, matrix.1, matrix.3)
}

let matrix = Matrix(1.1, 1.2, 2.1, 2.2);
println!("Matrix:\n{}", matrix);
// Matrix:
// ( 1.1 1.2 )
// ( 2.1 2.2 )

println!("Transpose:\n{}", transpose(matrix));
// Transpose:
// ( 1.1 2.1 )
// ( 1.2 2.2 )
  • これだけだと、次のようにtrasnposeを呼び出した後の変数matrixを再利用できなくなってしまう
let matrix = Matrix(1.1, 1.2, 2.1, 2.2);
println!("Matrix:\n{}", matrix);
println!("Transpose:\n{}", transpose(matrix));
println!("Matrix Again:\n{}", matrix);

// 以下のコンパイルエラー
error[E0382]: borrow of moved value: `matrix`
  --> src/main.rs:79:35
   |
73 |     let matrix = Matrix(1.1, 1.2, 2.1, 2.2);
   |         ------ move occurs because `matrix` has type `Matrix`, which does not implement the `Copy` trait
...
78 |     println!("Transpose:\n{}", transpose(matrix));
   |                                          ------ value moved here
79 |     println!("Matrix Again:\n{}", matrix);
   |                                   ^^^^^^ value borrowed here after move
  • rustにはオブジェクトの所有権があり、他の関数に渡してしまうとその後利用できなくなる
  • 解決策は以下のいずれか
    • Matrix構造体に #[derive(Debug, Copy, Clone)]としてCopyトレイトとCloneトレイトを追加する
    • transpose関数の引数で参照を渡すようにする
fn transpose(matrix: &Matrix) -> Matrix {
    Matrix(matrix.0, matrix.2, matrix.1, matrix.3)
}

println!("Matrix:\n{}", matrix);
println!("Transpose:\n{}", transpose(&matrix));
println!("Matrix Again:\n{}", matrix);
// 問題なく実行できる    

 

[まとめ]

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

ついに所有権の話がでてきた...!気をつけねば。

 

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

github.com