web-dev-qa-db-ja.com

寿命Rust

たまに、次の2つの方法のいずれかで呼び出すことができる関数を書きたいと思ったことがあります。

// With a string literal:
let lines = read_file_lines("data.txt");

// With a string pointer:
let file_name = ~"data.txt";
let lines = read_file_lines(file_name);

私の最初の推測は、パラメーター型に借用ポインター(&str)を使用することでしたが、それが機能しない場合(@str~strしか使用できません)、私は試しました以下は(Rustライブラリをコピーすることにより)動作しました。

fn read_file_lines<'a>(path: &'a str) -> ~[~str] {
    let read_result = file_reader(~Path(path));
    match read_result {
        Ok(file) => file.read_lines(),
        Err(e) => fail!(fmt!("Error reading file: %?", e))
    }
}

問題は、自分が何をしているか理解できないことです。私が収集できるものから(主にコンパイラエラーから)、制限のない有効期間を宣言し、それを使用してパスパラメータを記述しています(つまり、任意の有効期間をパラメータとして渡すことができます)。

そう:

  • 私の理解は漠然と正確ですか?
  • 生涯とは何ですか?それらの詳細はどこで確認できますか?
  • 上記の例のタイプ&strのパラメーターとタイプ&'a strのパラメーターの違いは何ですか?
  • そして、その間、'selfとは何ですか?

(私はRust 0.7を使用しています、それが答えに違いがある場合)

53
Daniel

Update 2015-05-16:元の質問のコードが古いバージョンのRustに適用されましたが、概念は同じです。この回答は、modern Rust syntax/librariesを使用するように更新されました。(_~[]_をVecおよび_~str_をStringに本質的に変更する最後にコード例を調整します。)

私の理解は漠然と正確ですか?
[...]
上記の例で、タイプ&strのパラメーターとタイプ& 'a strのパラメーターの違いは何ですか?

はい、そのような生涯は、基本的に「制限なし」と言っています。ライフタイムは、出力値を入力に接続する方法です。つまり、fn foo<'a, T>(t: &'a T) -> &'a Tは、footと同じライフタイムを持つポインター、つまり、ポインターが指すデータを返すと言いますtoはtと同じ期間有効です(まあ、厳密には、少なくとも同じ長さです)。これは基本的に、戻り値がtが指すメモリのサブセクションを指すことを意味します。

したがって、fn<'a>(path: &'a str) -> Vec<String>のような関数は、_{ let x = 1; return 2; }_を書くのと非常に似ています...これは未使用の変数です。

Rustは_&str_を書き込むときにデフォルトのライフタイムを割り当てます。これは、未使用の変数のライフタイムを書き込むこととまったく同じです。つまり、fn(path: &str) -> Vec<String>は、_'a_ sのバージョンと同じです。ライフタイムを終了する唯一の時間は、グローバルポインターを強制する必要がある場合(つまり、特別な_'static_ライフタイム)、または参照を返す必要がある場合(例_-> &str_)です。戻り値に有効期間がある場合にのみ可能です(これは、1つ以上の入力の有効期間、または_'static_でなければなりません)。

生涯とは何ですか?それらの詳細はどこで確認できますか?

ライフタイムとは、ポインタが指すデータが存在することが保証される期間です。グローバル変数は、「永久に」続くことが保証されています(そのため、特別な有効期間_'static_が与えられます)。それらを確認する1つのきちんとした方法は、次のとおりです。ライフタイムは、所有者が配置されているスタックフレームにデータを接続します。そのスタックフレームが終了すると、所有者はスコープから外れ、その値/データ構造への/へのポインターは無効になり、有効期間はコンパイラーがこれを推論するための方法です。 (スタックフレームビューでは、_@_に現在のタスクに関連付けられた特別なスタックフレームがあり、staticsに「グローバル」スタックフレームがあるかのようです)。

本のライフタイムの章 、そして この要点 (コードは現在古くなっていますが、概念はまだ真実です)もあります。ライフタイムを使用して、コピー/割り当ての必要を回避します(強力な安全性の保証:ポインターがぶら下がる可能性はありません)。

そして、私がそこにいる間、_'self_とは何ですか?

文字通り特別なことは何もありません。特定の場所ではタイプにライフタイムが必要です(たとえば、構造体/列挙型の定義とimpls内)。現在、_'self_と_'static_のみが受け入れられる名前です。 _'static_は常に有効なグローバルポインタを表し、_'self_は任意の有効期間を持つことができるものを表します。 (非[static)ライフタイムをself以外のものと呼ぶとエラーになるのはバグです。


全体として、私はそのような関数を次のように書きます:

_use std::fs::File;
use std::io::prelude::*;
use std::io::BufReader;
use std::path::Path;

fn read_file_lines(path: &Path) -> Vec<String> {
    match File::open(path) {
        Ok(file) => {
            let read = BufReader::new(file);
            read.lines().map(|x| x.unwrap()).collect()
        }
        Err(e) => panic!("Error reading file: {}", e)
    }
}

fn main() {
   let lines = read_file_lines(Path::new("foo/bar.txt"));
   // do things with lines
}
_
56
huon