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