関数とライフタイム

関数の引数や戻り値のライフタイムは完全に指定する必要がありますが、Rust ではほとんどの場合、いくつかの簡単なルールにより、ライフタイムを省略できます。これは推論ではなく、構文の省略形にすぎません。

  • ライフタイム アノテーションが付いていない各引数には、1 つのライフタイムが与えられます。
  • 引数のライフタイムが 1 つしかない場合、アノテーションのない戻り値すべてにそのライフタイムが与えられます。
  • 引数のライフタイムが複数あり、最初のライフタイムが self である場合、アノテーションのない戻り値すべてにそのライフタイムが与えられます。
#[derive(Debug)]
struct Point(i32, i32);

fn cab_distance(p1: &Point, p2: &Point) -> i32 {
    (p1.0 - p2.0).abs() + (p1.1 - p2.1).abs()
}

fn nearest<'a>(points: &'a [Point], query: &Point) -> Option<&'a Point> {
    let mut nearest = None;
    for p in points {
        if let Some((_, nearest_dist)) = nearest {
            let dist = cab_distance(p, query);
            if dist < nearest_dist {
                nearest = Some((p, dist));
            }
        } else {
            nearest = Some((p, cab_distance(p, query)));
        };
    }
    nearest.map(|(p, _)| p)
}

fn main() {
    let points = &[Point(1, 0), Point(1, 0), Point(-1, 0), Point(0, -1)];
    println!("{:?}", nearest(points, &Point(0, 2)));
}
This slide should take about 5 minutes.

この例では、cab_distance に関するライフタイムの記述は省略されています。

nearest 関数は、明示的なアノテーションを必要とする複数の参照を引数に含む関数のもう一つの例です。

返されるライフタイムについて嘘のアノテーションを付けるようにシグネチャを調整してみましょう。

fn nearest<'a, 'q>(points: &'a [Point], query: &'q Point) -> Option<&'q Point> {

そうするとコンパイルが通らなくなります。これは、すなわち、コンパイラがアノテーションの妥当性をチェックしているということを示すものです。ただし、これは生のポインタ(安全ではない)には当てはまりません。アンセーフRustを使用する場合に、これはよくあるエラーの原因となっています。

ライフタイムをどのような場合に使うべきか、受講者から質問を受けるかもしれません。Rust の借用では常にライフタイムを使用します。ほとんどの場合、省略や型推論 により、ライフタイムを記述する必要はありません。より複雑なケースでは、ライフタイム アノテーションを使用することであいまいさを解決できます。多くの場合、特にプロトタイピングでは、必要に応じて値をクローニングして所有データを処理する方が簡単です。