2017年7月13日木曜日

The Rust Programming Language 2nd 14日目

https://doc.rust-lang.org/book/second-edition/
Apache License Version 2.0

Generic Types, Traits, and Lifetimes

Validating References with Lifetimes

referenceにはかならずlifetime(寿命)という,referenceが有効であるスコープをもっているが,大方の場合それは明示されずコンパイラに推測される.lifetimeを特に指定する場合には,generic lifetime parametersを使う.lifetimeはRust独特の機能であって,時に非常に重要なので,この章でその基本的な概念を述べた後,19章で応用的なlifetimeの扱い方を学ぶ.

Lifetime Prevent Dangling References

dangling(宙ぶらりん) referenceは,すでに意味を失った変数へのreferenceで,これを放置すると,あるデータにアクセスしようとして他のデータにアクセスしてしまうことがある.lifetimeの目的はdanglin referenceが出来るのを防ぐことである.
例えばlist 10-16では内側の{ }で定義されたxへのreferenceをrに代入しているが,xは内側の{ }が終了すると同時に消えてしまうので,その時点でrも有効ではなくなる.つまりxのlifetimeは内側の{ }の中で,rのlifetimeは外側の{ }の中だから,その外でのreferenceは無効になる.
list 10-16

{
    let r;

    {
        let x = 5;
        r = &x;
    }

    println!("r: {}", r);
}

The Borrow Checker

list 10-16にlifetimeの注釈を加えてみる.
list 10-17

{
    let r;         // -------+-- 'a
                   //        |
    {              //        |
        let x = 5; // -+-----+-- 'b
        r = &x;    //  |     |
    }              // -+     |
                   //        |
    println!("r: {}", r); // |
                   //        |
                   // -------+
}

rのlifetimeを'a, xのlifetimeを'bで書いた.コンパイラはそれぞれのlifetimeを比較し,rが“`x““をborrowしているのを見つけて,エラーを出す.
list 10-18はdangling referenceを持たず,正常にコンパイルできる.
list 10-18

{
    let x = 5;            // -----+-- 'b
                          //      |
    let r = &x;           // --+--+-- 'a
                          //   |  |
    println!("r: {}", r); //   |  |
                          // --+  |       rustでは,宣言した順とは逆順に
}                         // -----+       変数が無効化されていくのだった

Generic Lifetimes in Functions

2つのstring sliceを引数に与えて,長い方のstring sliceを返す関数longestを考える.
longestの実装は後回しにして,例えばlongeestはlist 10-19のように利用できる.
src/main.rs list 10-19

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

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

は正常に動けば”abcd”を出力するはずだ.
list 10-20は longestの案だが,コンパイルできない.
src/main.rs list 10-19

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

shell

error[E0106]: missing lifetime specifier
   |
1  | fn longest(x: &str, y: &str) -> &str {
   |                                 ^ expected lifetime parameter
   |
   = help: this function's return type contains a borrowed value, but the
   signature does not say whether it is borrowed from `x` or `y`

エラーメッセージは返り値のreferenceがx, yどちらを指せばいいのかわからないと言っている.しかしプログラマもx, yのどちらが長いかは事前にわからないし,与えられる引数のlifetimeがどうであるかもわからない.そこで,generic lifetime parameterによってrefereneたちの間の関係性を記述し,borrow checkerを助けることにする.

Lifetime Annotation Syntax

lifetime annotationは変数のlifetimeそのものを変えることはできないが,複数のreferenceを関連付けることが出来る.構文としては,アポストロフィ`'とそれに続くlifetime parameterの名前(ふつう小文字1文字)を書く.'aが最も使われる書き方である.lifetime annotation自体は,referenceの&の直後に書く.たとえば

&i32          // a reference
&'a i32       // a reference with an explicit lifetime
&'a mut i32   // a mutable reference with an explicit lifetime

などと書く.

0 件のコメント:

コメントを投稿