第39回です。前回はこちら。
[第39回の様子]
2022/05/18に第39回を開催した。
内容としてはRust By Example 日本語版の「14.7. New Type Idiom」から「14.8.2. 関連型」に取り組んだ。
参加者は5人。安定していて良い。
[学んだこと]
- 14.7. New Type Idiom
- newtypeイディオムというやつがあるらしい。最近Twitterで話題の値オブジェクト的なやつだと思えばいいかな?
- プリミティブなi64型ではなく数値としての年数と日数を分けたい場合に以下のようにできる
struct Years(i64); struct Days(i64);
- これを利用すると、関数の引数の型チェックが効くので便利
struct Years(i64); struct Days(i64); impl Days { pub fn to_years(&self) -> Years { Years(self.0 / 365) } } fn old_enough(age: &Years) -> bool { age.0 >= 18 } fn main() { let age = Years(5); let age_days = Days(10000); println!("Old enough {}", old_enough(&age)); // false println!("Old enough {}", old_enough(&age_days.to_years())); // true }
- newtypeから値を取り出すときは、いつも通りタプルとしてアクセスするか、分解宣言を利用する
struct Years(i64); fn main() { let years = Years(42); let years_as_primitive_1: i64 = years.0; // タプル let Years(years_as_primitive_2) = years; // 分解 }
- 14.8. 関連型
- 関連型(associated items)というトレイトの拡張機能があるらしい
- 14.8.1. 関連型が必要になる状況
- 関連型はジェネリックなコンテナにトレイトを利用する場面で必要になる:まずは関連型を使わない書き方を見ていく
struct Container(i32, i32); // ジェネリックトレイト trait Contains<A, B> { fn contains(&self, _: &A, _: &B) -> bool; fn first(&self) -> i32; fn last(&self) -> i32; } // Container型にトレイトを実装する impl Contains<i32, i32> for Container { fn contains(&self, number_1: &i32, number_2: &i32) -> bool { (&self.0 == number_1) && (&self.1 == number_2) } fn first(&self) -> i32 { self.0 } fn last(&self) -> i32 { self.1 } } // 関連型を使わない面倒なポイント:AとBはCから自動的に決まって欲しい fn difference<A, B, C>(container: &C) -> i32 where C: Contains<A, B> { container.last() - container.first() } fn main() { let number_1 = 3; let number_2 = 10; let container = Container(number_1, number_2); println!("The difference is: {}", difference(&container)); // 7 }
- 14.8.2. 関連型
- 関連型を使うと、トレイトを以下のように書く
trait Contains { type A; type B; fn contains(&self, &Self::A, &Self::B) -> bool; } // ジェネリックトレイトではないので関数宣言がスッキリ fn difference<C: Contains>(container: &C) -> i32 { ... } // 比較のため修正前 fn difference<A, B, C>(container: &C) -> i32 where C: Contains<A, B> { ... }
- 関連型を使ったトレイトを実装する場合は以下のように書く
struct Container(i32, i32); trait Contains { type A; type B; fn contains(&self, _: &Self::A, _: &Self::B) -> bool; fn first(&self) -> i32; fn last(&self) -> i32; } impl Contains for Container { // 型を明示 type A = i32; type B = i32; // &i32の代わりに&Self::Aまたは&self::Bと書いても良い fn contains(&self, number_1: &i32, number_2: &i32) -> bool { (&self.0 == number_1) && (&self.1 == number_2) } fn first(&self) -> i32 { self.0 } fn last(&self) -> i32 { self.1 } }
- 確かにスッキリするけど、ライブラリとか作る場面じゃないと使わない気もする...どうなんだろう?
[まとめ]
モブプログラミングスタイルでRust dojoを開催した。
関連型、よくわからなかった。
今週のプルリクエストはこちら。