📝

ライフタイム

に公開

ラ~イフタァ~イム

関数の引数や、返り値に参照が含まれている場合、多くはライフタイムを指定しなけらばならない。
「参照元がドロップしているのに、参照しに行く」というのを防ぐため。(ダングリングポインタ)

fn test<'a>(data1: &'a str, data2: &'a str) -> &'a str {
    if data1.len() > data2.len() {
        data1
    } else {
        data2
    }
}

fn main() {
    let result;
    let data1 = String::from("monday");
    let data2 = String::from("tuesday");
    result = test(&data1, &data2);
    println!("{}", result); 
}

Q.なぜ、すべて<'a>(同じ)になっているのか?
A.すべての変数が同じ期間残っているから。
main()test()関数ではdata1data2の参照を引数であり、同スコープないにあるためライフタイムは同じになる。
また、返り値も'data1'の'data2'どちらかを参照という形で返すため、ライフタイムは同じでなければならない。
例えば、返り値を&'b strにしたとする。これは返り値が恐らく引数のどちらかになるのに、引数とライフタイムが違うためエラーになる。

頭が悪いのでこの結論に至るまで1日かかりました。なんとなくわかったのでヨシ!
後のわかったことですが、上記コードはコンパイラ様がどうやらライフタイムを推論してくれるから別にライフタイムを書く必要はないとのことでした。フ。

構造体とライフタイム

構造体に参照をフィールドとして含める場合、その参照が指すデータが構造体のインスタンスより長生きすることを保証する必要がある。

struct Person<'a> {
    name: &'a str,
}

fn example<'a>(s: &'a Person) {
    println!("Data: {}", s.name);
}

fn main() {
    let s = Person {name: "Alice"};
    example(&s);
}

トレイト境界とライフタイム

fn process_data<'a, T: AsRef<&'a str>>(data: T) -> &'a str {
    data.as_ref()
}

ライフタイムの省略・推論

省略

型推論と同じく、コンパイラがライフタイムを明らかである場合は記述する必要がない。
具体的に例えると、1.単一の入力参照を持つ場合、2.入力参照は複数だが、戻り値が一意に決まる場合など。もっとあると思いますが、私では思いつきませんでした、、、
1.単一の入力参照を持つ場合

fn get_first(x: &str) -> &str {
    x
}

fn main() {
    let s = "hello";
    let result = get_first(s);
    println!("Result: {}", result);
}

2.入力参照は複数だが、戻り値が一意に決まる場合

// fn get_first<'a, 'b>(x: &'a &str, y: &'b &str) -> &'a &str
fn get_first(x: &str, y: &str) -> &str {
    x
}

fn main() {
    let s1 = "hello";
    let s2 = "world";
    let result = get_first(s1, s2);
    println!("Result: {}", result);
}

推論

省略と使う場面が似てると思われる。使いどころはあまりよくわからなかったが、コードに冗長性がなくなり簡潔になります。

fn get_first(x: &'_ str, y: &'_ str) -> &'_ str { // ライフタイムを推論してくれる
    x
}

fn main() {
    let s1 = "hello";
    let s2 = "world";
    let result = get_first(s1, s2);
    println!("Result: {}", result);
}

ちなみに以下のコードは省略・推論できない。
longest()の返り値がxyになり、どちらのライフタイムを適用すればいいかわからないため。

fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() { x } else { y }
}

fn main() {
    let s1 = String::from("short");
    let s2 = String::from("longer");
    let result = longest(&s1, &s2);
    println!("Result: {}", result); // longer
}

まとめ

・難しい。
・トレイト境界が意味わかんなくてそれを調べるのに死ぬほど時間かかった。
・の割には雑。
<'_>の使いどころがわからん過ぎる。

Discussion