Rust dojo第46回を開催した

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

kdnakt.hatenablog.com

 

 

[第46回の様子]

2022/07/06に第46回を開催した。

 

内容としてはRust By Example 日本語版のライフタイムの章「15.4.6. ライフタイム境界」、「15.4.7. 圧縮」に取り組んだ。

 

参加者は自分を入れて4人。

またちょっと少なくなってきた気がする。

 

[学んだこと]

  • 15.4.6. ライフタイム境界
  • T: 'aのような形で、ジェネリック型にライフタイムを指定できる
    • この場合、Tは'aより長生きする必要がある
  • これをトレイト境界と同時に指定するとT: Debug + 'aのようになる
use std::fmt::Debug;

#[derive(Debug)]
struct Ref<a, T: 'a>(&'a T);
// (&'a T)='aというライフタイムのT型への参照を持つ
// T: 'a=Tは'aより長生きでなくてはならない
// Ref<'a, ..>=Refのライフタイムは

// Tは'aよりも長生きでなくてはならない
// 関数のライフタイムは'aを超えてはならない
fn print_ref<'a, T>(t: &'a T) where
    T: Debug + 'a {
    println!("`print_ref`: t is {:?}", t);
}

fn main() {
    let x = 7;
    let ref_x = Ref(&x);

    print_ref(&ref_x); // `print_ref`: t is Ref(7)
}
  • これだけだと、分かるような分からないような感じ...
  • print_ref()関数について、引数のところで&'a Tとあるから、Tのライフタイムが'aのライフタイムより長いのは分かる
  • それとT: Debug +'aは同じ意味な気がするのだが、どちらかだけでも動くのだろうか?
  • というわけで試してみる...とどちらも普通に動いてしまった。違いが良くわからない...
use std::fmt::Debug;

#[derive(Debug)]
struct Ref<'a, T: 'a>(&'a T);

// whereでのライフタイム指定なしパターン
fn print_ref2<a, T>(t: &'a T) where
    T: Debug {
    println!("`print_ref2 - no where constraint`: t is {:?}", t);
}

// 引数でのライフタイム指定なしパターン
fn print_ref3<'a, T>(t: & T) where
    T: Debug + 'a {
    println!("`print_ref3 - no param constraint`: t is {:?}", t);
}

fn main() {
    let x = 7;
    let ref_x = Ref(&x);

    print_ref2(&ref_x);
    // `print_ref2 - no where constraint`: t is Ref(7)
    
    print_ref3(&ref_x);
    // `print_ref3 - no param constraint`: t is Ref(7)
}
  • 15.4.7. 圧縮
  • ライフタイムには圧縮(coerce)という概念があるらしい
  • 長いものを短くして、そのままでは使えないところでも使えるようにしてくれる
// firstとsecondは同じライフタイムをもつ必要がある
fn multiply<a>(first: &'a i32, second: &'a i32) -> i32 {
    first * second
}

fn main() {
    let first = 2; // 長いライフタイム
    
    {
        let second = 3; // 短いライフタイム
        
        // ここではRustコンパイラがライフタイムを出来る限り短く見積もり、
        // 2つの参照をそのライフタイムに「圧縮」する
        println!("The product is {}", multiply(&first, &second)); // 6
    };
}
  • これ
// <'a: 'b, 'b>は「'aは最低でも'bと同じ長さ」と読める
// ここでは、ライフタイム'aの引数firstをとるが、ライフタイムを'bに圧縮して返す。
fn choose_first<'a: 'b, 'b>(first: &'a i32, _: &'b i32) -> &'b i32 {
    first
}

fn main() {
    let first = 2; // 長いライフタイム
    
    {
        let second = 3; // 短いライフタイム
        
        println!("{} is the first", choose_first(&first, &second)); // 2
    };
}
  • いい感じにしてくれる、ということだけはわかったけど使い所がよく分からない...
  • ライフタイムが短くなるということはfirstに再代入するとエラーになったりするのだろうか?と思ってやってみた
fn choose_second<'a: 'b, 'b>(_: &'a i32, second: &'b i32) -> &'b i32 {
    second
}

fn main() {
    let first = 2; // 長いライフタイム
                   
    let mut ref_first = &first;
    
    {
        let second = 3; // 短いライフタイム
        
        ref_first = choose_second(&first, &second);
    };
    println!("{:?}", ref_first);
}

// 以下のコンパイルエラー
error[E0597]: `second` does not live long enough
  --> src/main.rs:27:43
   |
27 |         ref_first = choose_second(&first, &second);
   |                                           ^^^^^^^ borrowed value does not live long enough
28 |     };
   |     - `second` dropped here while still borrowed
29 |     println!("{:?}", ref_first);
   |                      --------- borrow later used here
  • { ... }のブロックの中でsecondのライフタイムが終わるにも関わらず、外側で参照しようとするので、ライフタイムが足りずコンパイルエラーになってしまった
  • 圧縮ができるなら、反対に延長すればコンパイルエラーにならないのでは?と思ったがそれはできなかった
// aは最短でbと同じ長さだがbより長い(ミスマッチ)、のでbのライフタイムをaに伸ばすことはできない
fn choose_second<'a: 'b, 'b>(_: &'a i32, second: &'b i32) -> &'a i32 {
    second
}

fn main() {
    let first = 2; // 長いライフタイム
                   
    let mut ref_first = &first;
    
    {
        let second = 3; // 短いライフタイム
        
        ref_first = choose_second(&first, &second);
    };
    println!("{:?}", ref_first);
}

error[E0623]: lifetime mismatch
  --> src/main.rs:14:5
   |
13 | fn choose_second<'a: 'b, 'b>(_: &'a i32, second: &'b i32) -> &'a i32 {
   |                                 -------          ------- these two types are declared with different lifetimes...
14 |     second
   |     ^^^^^^ ...but data from `second` flows here

 

[まとめ]

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

ライフタイム...むずかしい...何もわからん...。

 

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

github.com