rustlifetime

Why does reassigning a function argument reference cause a lifetime error in Rust?


I'm trying to understand precisely how Rust lifetimes work. I've hit on a case I do not understand:

/// `outer` is a local stack variable of f1, should have the same lifetime as f1
fn f1(mut outer: &String) {
    println!("{outer}");
    let inner: String = "x".to_owned();

    outer = &inner;
    // error[E0597]: borrowed value does not live long enough
    // But it obviously does?? We don't return inner out of the function

    println!("{outer}");
}
// error[E0597]: - `inner` dropped here while still borrowed
// It seems like rust is trying to drop inner before outer? Which yeah fair in that case outer reference would outlive

Even though inner is dropped at the end of the function and thus doesn't outlive the original outer, I assumed this would be valid.

However, the following works (Playground):

fn main() {

    let outer_src = "hello ".to_owned();
    let outer = &outer_src;

    // Inner lifetime - simulating the function lifetime
    {
        // Simulating the stack variable of the f1
        let mut fn1_outer: &String = outer;

        // The same body as f1
        println!("{outer}");
        let fn1_inner: String = "x".to_owned();
        fn1_outer = &fn1_inner;
        println!("{fn1_outer}")

        // This works though for some reason?? What happens here I want to happen in the f1
    }

    println!("{outer}")
}

I have two theories how why this occurs:

Which of these is correct?

I suppose the possible generalized rules are:

  1. Argument variables must outlive all stack variables
  2. Argument variables' lifetimes are tied to the callee variables' lifetime

But this evidently works:

fn f1(mut outer: &String) {
    let mut fn1_outer: &String = outer;
    println!("{fn1_outer}");
    let fn1_inner: String = "x".to_owned();
    fn1_outer = &fn1_inner;
    println!("{fn1_outer}")
}

Solution

  • The lifetime of outer is the entirety of your function, while the lifetime of inner is the body of the function. Thus inner is dropped too soon. When you create a new variable fn1_outer (last example) its lifetime is inferred to be shorter than the function body.