目次

Rustの勉強[ライフタイム その2]

(更新: )ぎじゅつ

はじめに

#

NO IMAGEThe Rust Programming Language 日本語版 - The Rust Programming Language 日本語版
を読んでいる

  • ずっと眠い
  • もうすこし生活が楽になりたい

前回の振り返り

#
  • ライフタイムの復習
  • なぜ今これをやるのかを次にやる

今日読む場所

#

お勉強

#

メモ

#
fn main() {
    let string1 = String::from("abcd");
    let string2 = "xyz";

    let result = longest(string1.as_str(), string2);
    println!("The longest string is {}", result);
}

fn longest(x: &str, y: &str) -> &str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}
  • これがコンパイルエラーになるよね?って話をしている

    実際のところ、この関数の本体のifブロックはxへの参照を返し、elseブロックはyへの参照を返すので、 どちらなのか私たちにもわかりません!

  • たしかに当たり前にそうだね

    借用チェッカーが解析を実行できるように、参照間の関係を定義するジェネリックなライフタイム引数を追加しましょう。

  • 言いたいことはわかってた

    ライフタイム注釈は、少し不自然な記法です: ライフタイム引数の名前はアポストロフィー(')で始まらなければならず、 通常全部小文字で、ジェネリック型のようにとても短いです。多くの人は、'aという名前を使います。 ライフタイム引数注釈は、参照の&の後に配置し、注釈と参照の型を区別するために空白を1つ使用します。
    例を挙げましょう: ライフタイム引数なしのi32への参照、'aというライフタイム引数付きのi32への参照、 そして同じくライフタイム'aを持つi32への可変参照です。

&i32        // a reference
            // (ただの)参照
&'a i32     // a reference with an explicit lifetime
            // 明示的なライフタイム付きの参照
&'a mut i32 // a mutable reference with an explicit lifetime
  • なんか特殊例かなーって思ったけど普通にありえそうじゃん

さて、longest関数を例にライフタイム注釈を詳しく見ていきましょう。ジェネリックな型引数同様、 関数名と引数リストの間の山カッコの中にジェネリックなライフタイム引数を宣言します。 このシグニチャで表現したい制約は、引数の全ての参照と戻り値が同じライフタイムを持つことです

fn main() {
    let string1 = String::from("abcd");
    let string2 = "xyz";

    let result = longest(string1.as_str(), string2);
    println!("The longest string is {}", result);
}

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

この関数シグニチャでライフタイム引数を指定する時、渡されたり、返したりした、いかなる値のライフタイムも変更していないことを思い出してください。 むしろ、借用チェッカーは、これらの制約を守らない値全てを拒否するべきと指定しています。 longest関数は、xとyの正確な生存期間を知っている必要はなく、 このシグニチャを満たすようなスコープを'aに代入できることを知っているだけであることに注意してください。

  • んー解説が頭に入ってこないな
fn main() {
    // 長い文字列は長い
    let string1 = String::from("long string is long");
    // (訳注:この言葉自体に深い意味はない。下の"xyz"より長いということだけが重要)

    {
        let string2 = String::from("xyz");
        let result = longest(string1.as_str(), string2.as_str());
        // 一番長い文字列は{}
        println!("The longest string is {}", result);
    }
}

fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}
fn main() {
    let string1 = String::from("long string is long");
    let result;
    {
        let string2 = String::from("xyz");
        result = longest(string1.as_str(), string2.as_str());
    }
    println!("The longest string is {}", result);
}

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

このコードのコンパイルを試みると、こんなエラーになります:

$ cargo run
   Compiling chapter10 v0.1.0 (file:///projects/chapter10)
error[E0597]: `string2` does not live long enough
 --> src/main.rs:6:44
  |
6 |         result = longest(string1.as_str(), string2.as_str());
  |                                            ^^^^^^^ borrowed value does not live long enough
7 |     }
  |     - `string2` dropped here while still borrowed
8 |     println!("The longest string is {}", result);
  |                                          ------ borrow later used here

error: aborting due to previous error

For more information about this error, try `rustc --explain E0597`.
error: could not compile `chapter10`.

To learn more, run the command again with --verbose.

このエラーは、resultがprintln!文に対して有効であるためには、string2が外側のスコープの終わりまで有効である必要があることを示しています。 関数引数と戻り値のライフタイムを同じライフタイム引数'aで注釈したので、コンパイラはこのことを知っています。

  • あーわかった気がする
  • 当たり前のことを言っていたが、結局'aを返さないとコンパイルそのものは通らないって話しか

まとめ

#
  • ライフタイム自体は理解してたっぽくて満足
  • おそらく理解はしていたが、この`a記法を知らないとそもそものコンパイルが通らないよって話が主体らしい
  • 記法を理解すればOKそう
  • 次はここ