Rust dojo第48回を開催した

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

kdnakt.hatenablog.com

 

 

[第48回の様子]

2022/07/20に第48回を開催した。

 

内容としてはRust By Example 日本語版の「15.4.9. 省略」、「16. トレイト」に取り組んだ。

 

参加者は自分を入れて4人。久しぶりにドライバを担当した。

 

[学んだこと]

  • そういえば前回のstaticライフタイムの時に、予約されてるライフタイム名の一つですという話があった
  • 他にどんなのが?と思ったが、キーワードの説明を見る限り他に予約されてるライフタイム名はなさそうだった
  • 15.4.9. 省略
  • ライフタイムは通常可読性のために省略される
  • 次の2つの関数は同じライフタイムシグネチャをもつ
fn elided_input(x: &i32) {
    println!("`elided_input`: {}", x);
}

fn annotated_input<'a>(x: &'a i32) {
    println!("`annotated_input`: {}", x);
}

fn main() {
    let x = 3;

    elided_input(&x);
    annotated_input(&x);
}
  • 次の2つも同様
fn elided_pass(x: &i32) -> &i32 { x }

fn annotated_pass<'a>(x: &'a i32) -> &'a i32 { x }

fn main() {
    let x = 3;

    println!("`elided_pass`: {}", elided_pass(&x));
    println!("`annotated_pass`: {}", annotated_pass(&x));
}
  • つまり前回staticライフタイムの圧縮で使った関数も同様にライフタイムを省略できる
static NUM: i32 = 18;

fn coerce_static<'a>(_: &'a i32) -> &'a i32 {
    &NUM
}

fn coerce_static2(_: &i32) -> &i32 {
    &NUM
}
  • 省略できて便利、というよりも意識してなかったところが実はライフタイムに関係していた、というのが気持ち悪いというかちゃんと教えてくれないと困るな、というか、そんな気分。
  • 16. トレイト
  • これまで、何度か出てきたけど、ようやくトレイトのちゃんとした説明がある
  • トレイトは任意の型に対して定義されたメソッドの集まり
    • メソッド同士はお互いにアクセス可能
  • トレイトはあらゆるデータ型に実装可能
  • 下記はAnimalトレイトをSheepに実装する例
// 構造体
struct Sheep { naked: bool, name: &'static str }

// トレイト
trait Animal {
    // 関連関数(ファクトリメソッド)
    fn new(name: &'static str) -> Self;

    // メソッド
    fn name(&self) -> &'static str;
    fn noise(&self) -> &'static str;

    // デフォルトの挙動を定義したメソッド
    fn talk(&self) {
        println!("{} says {}", self.name(), self.noise());
    }
}

impl Sheep {
    fn is_naked(&self) -> bool {
        self.naked
    }

    fn shear(&mut self) {
        if self.is_naked() {
            println!("{} is already naked...", self.name());
        } else {
            println!("{} gets a haircut!", self.name);
            self.naked = true;
        }
    }
}

impl Animal for Sheep {
    // Selfは実装対象の型: ここではSheep
    fn new(name: &'static str) -> Sheep {
        Sheep { name: name, naked: false }
    }

    fn name(&self) -> &'static str {
        self.name
    }

    fn noise(&self) -> &'static str {
        if self.is_naked() {
            "baaaaah?"
        } else {
            "baaaaah!"
        }
    }
    
    // トレイトメソッドはオーバーライド可能
    fn talk(&self) {
        println!("{} pauses briefly... {}", self.name, self.noise());
    }
}
  • Sheepを利用してみるとこんな感じ
fn main() {
    // Animalトレイトの関連関数を呼び出す
    // 実際のデータ型が不明なので、左辺に型アノテーションが必須
    let dolly: Sheep = Animal::new("Dolly");

    dolly.talk(); // baaaaah!
    
    // Sheepのnew関数を呼ぶ場合は左辺のアノテーション不要
    let mut merry = Sheep::new("Merry");
    merry.shear();
    merry.talk(); // baaaaah?
}
fn main() {
    let mut dolly = Animal::new("Dolly");
}

// 以下のコンパイルエラー
error[E0282]: type annotations needed
  --> src/main.rs:78:5
   |
74 |     let mut dolly = Animal::new("Dolly");
   |         --------- consider giving `dolly` a type
...
78 |     dolly.talk();
   |     ^^^^^ cannot infer type
   |
   = note: type must be known at this point
  • Animalはトレイトであってデータ型ではないので、左辺に利用できない
  • トレイトはコンパイル時にはどんなフィールドをもつかわからず、メモリ量を決定できないためこのようになってしまう。Boxを使ってヒープ領域を使うようにすれば回避できるが、コードが複雑になってしまうのでお預け。
fn main() {
    let mut dolly: Animal = Animal::new("Dolly");
}

// 同時に3つのエラーがでる
// 1つ目のコンパイルエラー
error[E0038]: the trait `Animal` cannot be made into an object
  --> src/main.rs:74:20
   |
74 |     let mut dolly: Animal = Animal::new("Dolly");
   |                    ^^^^^^ `Animal` cannot be made into an object
   |
note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
  --> src/main.rs:7:8

// 2つ目
error[E0277]: the size for values of type `dyn Animal` cannot be known at compilation time
  --> src/main.rs:74:9
   |
74 |     let mut dolly: Animal = Animal::new("Dolly");
   |         ^^^^^^^^^ doesn't have a size known at compile-time
   |
   = help: the trait `Sized` is not implemented for `dyn Animal`
   = note: all local variables must have a statically known size
   = help: unsized locals are gated as an unstable feature

// 3つ目
error[E0277]: the size for values of type `dyn Animal` cannot be known at compilation time
  --> src/main.rs:74:29
   |
74 |     let mut dolly: Animal = Animal::new("Dolly");
   |                             ^^^^^^^^^^^ doesn't have a size known at compile-time
   |
   = help: the trait `Sized` is not implemented for `dyn Animal`
   = note: the return type of a function must have a statically known size

 

[まとめ]

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

モヤモヤしたままライフタイムの章は終わってしまった。結局スコープとの違いもよくわからない...。

 

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

github.com