Rust dojo第39回を開催した

第39回です。前回はこちら。

kdnakt.hatenablog.com

 

 

[第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; // 分解
}
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
}
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を開催した。

関連型、よくわからなかった。

 

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

github.com