はじめに
NO IMAGEThe Rust Programming Language 日本語版 - The Rust Programming Language 日本語版
を読んでいる
色々やってて後回しになっていて自分にムカついた
こういうのが一番自分に腹立つので継続の優先順位は上げとこう
今日読む場所
NO IMAGEライフタイムで参照を検証する - The Rust Programming Language 日本語版
これ
お勉強
メモ
ここまで、所有された型を保持する構造体だけを定義してきました。構造体に参照を保持させることもできますが、 その場合、構造体定義の全参照にライフタイム注釈を付け加える必要があるでしょう。
- ナニーーーーーー
- 構造体に参照定義できるかよ
struct ImportantExcerpt<'a> {
part: &'a str,
}
fn main() {
// 僕をイシュマエルとお呼び。何年か前・・・
let novel = String::from("Call me Ishmael. Some years ago...");
// "'.'が見つかりませんでした"
let first_sentence = novel.split('.').next().expect("Could not find a '.'");
let i = ImportantExcerpt {
part: first_sentence,
};
}
- 書き方キモすぎる
- なんか混乱して来たな、整理しよう
- geminiと格闘した結果わかった
struct User<'a> {
name_a: String, // 所有権(ライフタイムに縛られない)
name_b: &'a str, // 借用('a の間だけ有効)
name_c: &'a str, // 借用('a の間だけ有効)
}
- こういうふうに書けるのね、理解した
struct Profile<'a, 'b> {
short_bio: &'a str, // 短い寿命でもOK
long_bio: &'b str, // 長い寿命が必要
}
- おーこうやってライフタイムをわけられるんだ
- やっとジェネリクスと同列に見えてきた、なるほど
NO IMAGEライフタイムで参照を検証する - The Rust Programming Language 日本語版
fn first_word<'a>(s: &'a str) -> &'a str {
多くのRustコードを書いた後、Rustチームは、Rustプログラマが、 特定の場面で何度も同じライフタイム注釈を入力していることを発見しました。これらの場面は予測可能で、 いくつかの決まりきったパターンに従っていました。開発者はこのパターンをコンパイラのコードに落とし込んだので、 このような場面には借用チェッカーがライフタイムを推論できるようになり、明示的な注釈を必要としなくなったのです。
ここで、このRustの歴史話が関係しているのは、他にも決まりきったパターンが出現し、コンパイラに追加されることもあり得るからです。 将来的に、さらに少数のライフタイム注釈しか必要にならない可能性もあります。
- これ理解してないとなんで飛ばされてんだってなるな
最初の規則は、参照である各引数は、独自のライフタイム引数を得るというものです。換言すれば、 1引数の関数は、1つのライフタイム引数を得るということです: fn foo<'a>(x: &'a i32); 2つ引数のある関数は、2つの個別のライフタイム引数を得ます: fn foo<'a, 'b>(x: &'a i32, y: &'b i32); 以下同様。
2番目の規則は、1つだけ入力ライフタイム引数があるなら、そのライフタイムが全ての出力ライフタイム引数に代入されるというものです: fn foo<'a>(x: &'a i32) -> &'a i32。
3番目の規則は、複数の入力ライフタイム引数があるけれども、メソッドなのでそのうちの一つが&selfや&mut selfだったら、 selfのライフタイムが全出力ライフタイム引数に代入されるというものです。 この3番目の規則により、必要なシンボルの数が減るので、メソッドが遥かに読み書きしやすくなります。
- これやばいね、何言ってるか全然頭に入ってこない
コンパイラの立場になってみましょう
- あ、はい
シグニチャは、参照に紐づけられるライフタイムがない状態から始まります:
fn first_word(s: &str) -> &str {
そうして、コンパイラは最初の規則を適用し、各引数が独自のライフタイムを得ると指定します。 それを通常通り'aと呼ぶので、シグニチャはこうなります:
fn first_word<'a>(s: &'a str) -> &str {
1つだけ入力ライフタイムがあるので、2番目の規則を適用します。2番目の規則は、1つの入力引数のライフタイムが、 出力引数に代入されると指定するので、シグニチャはこうなります:
fn first_word<'a>(s: &'a str) -> &'a str {
もうこの関数シグニチャの全ての参照にライフタイムが付いたので、コンパイラは、 プログラマにこの関数シグニチャのライフタイムを注釈してもらう必要なく、解析を続行できます。
別の例に目を向けましょう。今回は、リスト10-21で取り掛かったときにはライフタイム引数がなかったlongest関数です:
fn longest(x: &str, y: &str) -> &str {
最初の規則を適用しましょう: 各引数が独自のライフタイムを得るのです。今回は、 1つではなく2つ引数があるので、ライフタイムも2つです:
fn longest<'a, 'b>(x: &'a str, y: &'b str) -> &str {
2つ以上入力ライフタイムがあるので、2番目の規則は適用されないとわかります。また3番目の規則も適用されません。 longestはメソッドではなく関数なので、どの引数もselfではないのです。3つの規則全部を適用した後でも、 まだ戻り値型のライフタイムが判明していません。このために、リスト10-21でこのコードをコンパイルしようとしてエラーになったのです: コンパイラは、ライフタイム省略規則全てを適用したけれども、シグニチャの参照全部のライフタイムを計算できなかったのです。
- geminiに聞いて理解した
- 参照を返すライフタイムに全て3つを適応して、最初は引数が1つなのでライフタイムを割り当てて、そのライフタイムを戻り値にも割り当てるだけっていうシンプルな考え
- これが引数2つで戻り値が1つの場合はそもそも2つのライフタイムのどちらを割り当てるか明記しなければコンパイラは選別できないのでエラーになると
まとめ
- ライフタイムを雰囲気でやっていたが、
<T>と同じ考えで<'a>って書けば指定できるって理解してから問題なくなった - 法則の話も長く書いてあるから意味が不明なだけでこれでわかった
- 次はやっとライフタイム最後かな?
- 思っていたより結構ムズい。。。