Rust dojo第9回を開催した

第9回です。

前回はこちら。

kdnakt.hatenablog.com

 

 

[第9回の様子]

2021/09/08に第9回を開催した。

 

内容としてはRust By Example 日本語版の「3. カスタム型」、「3.1. 構造体」を読んで手を動かした。残念ながら最後の演習までは時間が足りず。

 

参加者は全部で12人。なんかすごい増えたぞ!

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

 

Slackでのやりとりはちょっと減ってきた。

 

いらっしゃいませー。

f:id:kidani_a:20210910021313p:plain

 

レポートありがとうございます!

f:id:kidani_a:20210910021000p:plain

 

復習してえらい!

f:id:kidani_a:20210910021230p:plain

 

[学んだこと]

  • 3. カスタム型
  • カスタム型はstructもしくはenumを使う
  • 他に定数を定義するのにstaticとかconstが使える(詳細は3.3で後述)
  • 3.1. 構造体
  • まずはstructで定義する構造体:ユニット、タプル、構造体の3種類ある
  • ユニットはフィールドを持たず、ジェネリック型を使う:よくわからないけど多分14章がジェネリクスなので、そこでまた出てきそう
// ユニットの例
struct Nil;

// インスタンス化
let _nil = Nil;
  • タプルは名前付きタプル
// フィールドに名前がないのがタプル
struct Pair(i32, f32);
  • 構造体はフィールドに名前がある
#[derive(Debug)]
struct Person<'a> {
    name: &'a str,
    age: u8,
}
  • ここで'aはnameがstrの参照なので、Personと寿命を揃えるために必要らしい
  • Personのインスタンスを作成する場合は次のようになる
let name = "Peter";
let age = 27;
let peter = Person { name, age };

println!("{:?}", peter); // Person { name: "Peter", age: 27 }
  • 変数nameの型はstrだからPersonを生成する際に&nameのようにして参照を渡す必要があるのでは?と思ったがそうではないらしい。フィールド名と一致している場合はよろしくやってくれるのだろうか。
let peter = Person { &name, age };

// ↑だけだとコンパイルエラー
error: expected identifier, found `&`
  --> src/main.rs:37:26
   |
37 |     let peter = Person { &name, age };
   |                 ------   ^ expected identifier
   |                 |
   |                 while parsing this struct

error[E0063]: missing field `name` in initializer of `Person<'_>`
  --> src/main.rs:37:17
   |
37 |     let peter = Person { &name, age };
   |                 ^^^^^^ missing `name`

error: aborting due to 2 previous errors
  • 以下のようにフィールド名を明示すると動く
let peter = Person { name: &name, age };
  • 構造体をほかの構造体のフィールドにもできる
struct Point {
    x: f32,
    y: f32,
}

struct Rectangle {
    top_left: Point,
    bottom_right: Point,
}
  • 構造体を作る際に、別のインスタンスのフィールドの値を転用できる:フィールド数が多い場合は便利かも
let point: Point = Point { x: 10.3, y: 0.4 };
println!("point coordinates: ({}, {})", point.x, point.y); // 10.3 0.4

let bottom_right = Point { x: 5.2, ..point };
println!("second point: ({}, {})", bottom_right.x, bottom_right.y); // 5.2 0.4
  • ..pointの部分が後ろにあるとなんかそっちの値でxが上書きされそうで嫌だったが、..pointを先に書くとコンパイルエラーとなってしまった
let bottom_right = Point { ..point, x: 5.2 };

// 以下のコンパイルエラー。元になる構造体は必ず最後でないとダメらしい
error: cannot use a comma after the base struct
  --> src/main.rs:52:32
   |
52 |     let bottom_right = Point { ..point, x: 5.2 };
   |                                ^^^^^^^- help: remove this comma
   |
   = note: the base struct must always be the last field
  • 構造体のフィールドを別の変数に分割代入する場合は、let 構造体名 { 元フィールド名: 代入先の変数名, ... } = インスタンス; のように書く
struct Point {
    x: f32,
    y: f32,
}
let point: Point = Point { x: 10.3, y: 0.4 };

// 分割代入
let Point { x: top_edge, y: left_edge } = point;
println!("{} {}", top_edge, left_edge); // 10.3 0.4
  • 分割代入する元の構造体の型は分かっているのだから、構造体名を明示しなくてもよいのでは?と思い実験してみたがコンパイルエラーとなった
let { x: top_edge, y: left_edge } = point;

// 以下のエラー
error: expected pattern, found `{`
  --> src/main.rs:60:9
   |
60 |     let { x: top_edge, y: left_edge } = point;
   |         ^ expected pattern
  • タプルも分割代入できる(元フィールド名がないので、代入先変数名のみ)
struct Pair(i32, f32);
let pair = Pair(1, 0.1);
println!("pair contains {:?} and {:?}", pair.0, pair.1); // 1 0.1

// 分割代入
let Pair(integer, decimal) = pair;
println!("pair contains {:?} and {:?}", integer, decimal); // 1 0.1

 

[まとめ]

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

来週は演習からだ。楽しみ!

 

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

github.com