第46回です。前回はこちら。
[第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を開催した。
ライフタイム...むずかしい...何もわからん...。
プルリクエストはこちら。