Lifetime

Lifetime is a named region of the code that a reference must be valid for.

A part of memory might be drop and reinitialize in some part of the program, and a reference should not outlive its owner. So we need lifetime to indicate that a reference is valid during paths of executions.

In most of the time, lifetime can be inferred by rust compiler. But when our code cross the function scope boundary or thread boundary, things will get complicate. No one but you can guarantee that a reference's owner can live long enough till all it's children reference are eliminated.

fn as_str(d: &u32) -> &str {
  let s = format!("{d}")
  &s
}

This is a wrong example, as variable s can not live long enough as the d variable's owner. Desugar the above example we will get:

fn as_str<'a>(d: &'a u32) -> &'a str {
  'b: {
    let s = format!("{d}")
    &'a s
  }
}

In the above example, the s variable are live in the 'b lifetime scope, however it want to return a reference that live in 'a lifetime scope. It is invalid, cuz s is dropped after the variable s return. The &s can not outlive its owner s.

This is what lifetime analysis did.

fn main() {
    let mut v = vec![1; 3];
		let a = &v[0];
		v.push(2);
		println!("{a}");
}

In the above code example, we let variable a to be an pointer to the first element of the vector v . Then we push a new value to the vector and print the a variable.

The code above can’t be compiled. Because when we push new value into vector, it might dynamically reallocate memory, so the memory address a point to is not guarantee to be as the same address as the before did. But how does rust know this?

De-sugar the above example, we can get:

fn main() {
	a': {
    let mut v = vec![1; 3];
		b': {
	    let a = &v[0];
		  c': {
				v.push(2);
			}
			println!("{a}");
		}
	}
}

The variable and type is nonsense to rustc compiler, but lifetime make sense. We can see a live in b' lifetime scope, c' lifetime is contained in b' lifetime. However we are going to make a mutable reference in the c' lifetime scope. This is not allowed because it breaks our rule. There is a shared reference in b' scope and not consumed yet.

Current limit

fn get_default<'m, K, V>(map: &'m mut HashMap<K, V>, key: K) -> &'m mut V
where
    K: Clone + Eq + Hash,
    V: Default,
{
    match map.get_mut(&key) {
        Some(value) => value,
        None => {
            map.insert(key.clone(), V::default());
            map.get_mut(&key).unwrap()
        }
    }
}

Code like above can’t be compiled, but we know it is okay. It will be fixed in future, currently we can use https://github.com/rust-lang/polonius to use new borrow checker model.

Elided lifetime parameter (隐式 lifetime 的规则)