End of function return: not live long enough

Can anyone explain why below happens? I thought x.borrow().val returns copy of i32 typed value, instead of referencing the memory pointed by x?

use std::cell::RefCell;

// Definition for a binary tree node.
#[derive(Debug, PartialEq, Eq)]
pub struct Foo {
    pub val: i32,
}

pub fn demo() -> i32 {
    let x = RefCell::new(Foo{ val: 10 });
    x.borrow().val
}

playground

Compiling playground v0.0.1 (/playground)
error[E0597]: `x` does not live long enough
  --> src/lib.rs:11:5
   |
11 |     x.borrow().val
   |     ^---------
   |     |
   |     borrowed value does not live long enough
   |     a temporary with access to the borrow is created here ...
12 | }
   | -
   | |
   | `x` dropped here while still borrowed
   | ... and the borrow might be used here, when that temporary is dropped and runs the destructor for type `Ref<'_, Foo>`
   |
   = note: the temporary is part of an expression at the end of a block;
           consider forcing this temporary to be dropped sooner, before the block's local variables are dropped
help: for example, you could save the expression's value in a new local variable `x` and then make `x` be the expression at the end of the block
   |
11 |     let x = x.borrow().val; x
   |     ^^^^^^^               ^^^

Besides, can anyone help me understand what does this mean from above error message?

the temporary is part of an expression at the end of a block;
           consider forcing this temporary to be dropped sooner, before the block's local variables are dropped

It is a "feature" of rust 1.0. End of block expressions don't follow the intuitive order you would expect items to get dropped in. (A language change would break existing code.)

struct Dr(i32);
impl Dr {  fn val(&self) -> i32 { self.0 } }
impl Drop for Dr {
    fn drop(&mut self) { println!("drop {}", self.0); }
}
pub fn foo() -> i32 {
    let _x = Dr(1);
    let _y = Dr(2);
    let _z = Dr(3);
    Dr(4).val()
}
2 Likes

You borrow the wrapped (in RefCell) value val. But as the compiler suggests you try to use borrowed (from x) value after x get dropped. That's why the compiler says x does not live long enough.

The message

the temporary is part of an expression at the end of a block;
consider forcing this temporary to be dropped sooner...

is a suggestion what you could do if you want to return the wrapped value. Since i32 implements Copy you could save (copy) the val in another var and return it at the end of the function body. For example,

pub fn demo() -> i32 {
   let x = RefCell::new(Foo { val: 10 });
   let y = x.borrow().val;
   y
}

or without y

pub fn demo() -> i32 {
   let x = RefCell::new(Foo { val: 10 });
   let x = x.borrow().val;
   x
}

Thanks!

do you mean x is dropped before val is returned?

drop x
return val

vs

return val
drop x

Is the latter approach is the intuitive order that you refer to?

Thanks! I'm using memory pointed by x, which is destructed before returned?

It seems I'm doing things like below?

return x.borrow().val

is translated into

x.drop();
return x.borrow().val

?

Intuitively (from assembly level), rust would drop or clean up before return call ret.

further discussion moves to the topic below: