Borrow One
In this example find_nearest takes in multiple borrows but returns only one of
them. The lifetime annotations explicitly tie the returned borrow to the
corresponding argument borrow.
// Copyright 2025 Google LLC
// SPDX-License-Identifier: Apache-2.0
#[derive(Debug)]
struct Point(i32, i32);
/// Searches `points` for the point closest to `query`.
/// Assumes there's at least one point in `points`.
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 // What happens if we do this instead?
}
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` isn't borrowed at this point.
drop(query);
dbg!(nearest);
}
-
It may be helpful to collapse the definition of
find_nearestto put more focus on the signature of the function. The actual logic in the function is somewhat complex and isn’t important for the purpose of borrow analysis. -
When we call
find_nearestthe returned reference doesn’t borrowquery, and so we are free to drop it whilenearestis still active. -
But what happens if we return the wrong borrow? Change the last line of
find_nearestto returnqueryinstead. Show the compiler error to the students. -
The first thing we have to do is add a lifetime annotation to
query. Show students that we can add a second lifetime'btofind_nearest. -
Show the new error to the students. The borrow checker verifies that the logic in the function body actually returns a reference with the correct lifetime, enforcing that the function adheres to the contract set by the function’s signature.
More to Explore
-
The “help” message in the error notes that we can add a lifetime bound
'b: 'ato say that'bwill live at least as long as'a, which would then allow us to returnquery. This is an example of lifetime subtyping, which allows us to return a longer lifetime where a shorter one is expected. -
We can do something similar by returning a
'staticlifetime, e.g., a reference to astaticvariable. The'staticlifetime is guaranteed to be longer than any other lifetime, so it’s always safe to return in place of a shorter lifetime.