Lifetimes in return variants: `return x;` or `let a=x; return a` VS `return x` or `x`

Hello everyone.
In my journey to learn Rust, I am stuck trying to understand why this makes sense, instead of this.

I am hoping someone would be kind enough to explain the why and what is really going on here.

To reiterate:

  • by using return x;(with the semicolon) in that playground example, where x is possible.drain().filter(is_valid_position).collect(), there are no compilation errors due to compiler knowing that the lifetime of possible.drain() ends on that same line, which means it ends before the lifetime of possible(which is at end of function). Note: an equivalent to return x; , at least in this case, would be let a=x; return a (semicolon after return a is irrelevant here).
  • however, in the other case: by using x or, what I understand to be its equivalent, return x (without semicolons), the compiler complains error[E0597]: possible does not live long enough seemingly because possible.drain() and possible both have lifetimes which end at the same place which is at function end, as I understand it. In which case I have to ask why doesn't this apply then note: values in a scope are dropped in the opposite order they are created ? Shouldn't the compiler know to do that ? Maybe I'm missing critical information about this. Please help me understand this.

Just in case playground or gist aren't available in the future, or for other reasons, here is that example code, duplicated here:

use std::collections::HashSet;

type Position = (usize, usize);

const SIZE: usize = 19;

fn is_valid_position(p: &Position) -> bool {
    let x = p.0;
    let y = p.1;
    x < SIZE && y < SIZE
}

fn get_neighbours(x: usize, y: usize) -> HashSet<Position> {
    let mut possible: HashSet<Position> = HashSet::new();
    possible.insert((x.wrapping_sub(1), y));
    possible.insert((x + 1, y));
    possible.insert((x, y.wrapping_sub(1)));
    possible.insert((x, y + 1));
    possible.drain().filter(is_valid_position).collect()
}

fn main() {
    println!("{:?}", get_neighbours(0, 1))
}

Cheers!

The root issue is https://github.com/rust-lang/rust/issues/21114, which also has much more documentation about the reasons and why it is hard to fix.

4 Likes

Thanks!

On another note, I've found something(NLL) that, at the very least, helps me understand the difference between lifetimes and scopes.