1 つを借用する

この例では find_nearest は複数の借用を受け取りますが、そのうち 1 つだけを返します。ライフタイム注釈により、返される借用が対応する引数の借用に明示的に結び付けられています。

// Copyright 2025 Google LLC
// SPDX-License-Identifier: Apache-2.0

#[derive(Debug)]
struct Point(i32, i32);

/// `points` から `query` に最も近い点を探します。
/// `points` には少なくとも 1 つの点があると仮定します。
fn find_nearest<'a>(points: &'a [Point], query: &Point) -> &'a Point {
    fn cab_distance(p1: &Point, p2: &Point) -> i32 {
        (p1.0 - p2.0).abs() + (p1.1 - p2.1).abs()
    }

    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).unwrap()
    // query // 代わりにこれを行うとどうなりますか?
}

fn main() {
    let points = &[Point(1, 0), Point(1, 0), Point(-1, 0), Point(0, -1)];
    let query = Point(0, 2);
    let nearest = find_nearest(points, &query);

    // この時点では、`query` は借用されていません。
    drop(query);

    dbg!(nearest);
}
  • find_nearest の定義を折りたたんで、関数シグネチャにより注目できるようにすると役立つかもしれません。関数内の実際のロジックはやや複雑で、借用解析という目的においては重要ではありません。

  • find_nearest を呼び出したとき、返される参照は query を借用しないため、nearest がまだ有効な間でも query を破棄できます。

  • では、誤った借用を返すとどうなるでしょうか? find_nearest の最後の行を変更して、代わりに query を返すようにしてください。コンパイラエラーを受講者に見せてください。

  • 最初にやるべきことは、query にライフタイム注釈を追加することです。find_nearest に 2 つ目のライフタイム 'b を追加できることを受講者に示してください。

  • 新しいエラーを受講者に見せてください。借用チェッカーは、関数本体のロジックが実際に正しいライフタイムを持つ参照を返していることを検証し、その関数が関数シグネチャで定められた契約に従うように強制します。

さらに探る

  • エラー内の “help” メッセージには、ライフタイム境界 'b: 'a を追加して 'b が少なくとも 'a と同じだけ生存することを示せるとあり、その場合は query を返せるようになります。これはライフタイムのサブタイピングの例であり、より短いライフタイムが期待される場所で、より長いライフタイムを返せるようにするものです。

  • 'static ライフタイムを返すことでも、たとえば static 変数への参照を返すことで、同様のことができます。'static ライフタイムは他のどのライフタイムよりも長いことが保証されているため、より短いライフタイムの代わりに返しても常に安全です。