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.

#[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);
}
This slide should take about 5 minutes.
  • It may be helpful to collapse the definition of find_nearest to 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_nearest the returned reference doesn’t borrow query, and so we are free to drop it while nearest is still active.

  • But what happens if we return the wrong borrow? Change the last line of find_nearest to return query instead. 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 'b to find_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.

  • The “help” note in the error notes that we can add a lifetime bound 'b: 'a to say that 'b will live at least as long as 'a, which would then allow us to return query. On the next slide we’ll talk about lifetime variance, which is the rule that allows us to return a longer lifetime when a shorter one is expected.