Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

rust

lifetime

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:

  • Drop order after the end of function attempts to drop argument variables, before dropping other stack variables, which causes a problem in my code
  • The function argument's lifetime is tied to the callee, not to the function itself. And it is impossible to re-assign it to a shorter lifetime.

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}")
}
like image 792
Meowxiik Avatar asked Mar 15 '26 06:03

Meowxiik


1 Answers

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.

like image 129
Patrick Schlieker Avatar answered Mar 17 '26 03:03

Patrick Schlieker



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!