Why can't I refer to the object in refcell

struct A {
    a: RefCell<f64>,
}

impl A {
    fn get_a(&self) -> &f64 {
        &self.a.borrow()
    }
}

Its error is:

error[E0515]: cannot return reference to temporary value

RefCell<T> returns a wrapper type Ref<'a, T>, which implements Deref<Target=T>, but its not &'a T, the wrapper type is a non trivial type, it need to keep track of the count of borrows from the same RefCell and keep the rust semantic of "no shared mutability" at runtime.

this line of code auto deref the Ref wrapper and reborrow it, resulting an reference to an temporary variable, so you can't return the reference to it.

you can return the Ref wrapper type instead, of you can use a scoped api which accepts a continuation/callback closure as argument, instead of returning a reference:

fn get_a(&self) -> Ref<'_, f64> {
    self.a.borrow()
}

fn do_something_with_a<R, F: FnOnce(&f64) -> R>(&self, f: F) -> R {
    f(self.a.borrow())
}
3 Likes
  • self.a.borrow() is actually RefCell::<f64>::borrow(&'tmp self.a) -> Ref<'tmp, f64>
  • &'return Ref<'tmp, f64> can be coerced to &'return f64
    since Deref::deref<'return>(&'return Ref<'tmp, f64>) -> &'return f64,
    which requires 'tmp: 'return and 'tmp ≠ 'return [1], i.e. 'tmp strictly outlives 'return.
    Although 'tmp might live as long as &self,
    'return, which can't live as long as &self lives due to the strict outliveness relationship, derives from the function, thus can't be returned from the function. That's what the error says.
error[E0515]: cannot return reference to temporary value
 --> src/lib.rs:8:9
  |
8 |         &self.a.borrow()
  |         ^---------------
  |         ||
  |         |temporary value created here
  |         returns a reference to data owned by the current function

  1. because the signature fn deref(&self) -> &Self::Target states for any lifetime of &self, it returns &Self::Target. ↩︎

1 Like

The immediate problem with your code is that it is never correct to return the address of a function call's result. The result of a function call is a temporary, so its address won't be valid once the function returns.

What you probably meant to do instead was:

fn get_a(&self) -> &f64 {
    &*self.a.borrow()
}

ie., get to the inner value by dereferencing the smart pointer given by .borrow(), and take the address of that.

However, this doesn't work, and it doesn't make sense, either. If you think about logically, being able to obtain a direct reference to the wrapped value would completely defeat the purpose of RefCell.

If you could obtain such a reference, then the cell couldn't count how many borrows there are and how many ended, since a simple reference wouldn't signal to its originating RefCell when it goes out of scope. Therefore, you could end up eg. borrowing the cell immutably and mutably at the same time, causing shared mutability, which is unsound.

3 Likes

Maybe, to rephrase this more accurately, one should say “obtaining a direct reference to the wrapped value without any guard object present anymore” would defeat the purpose of RefCell, since of course short-lived direct references can be obtained ^^

(Completely preventing [safe] direct references to the wrapped object is what Cell would do instead.)

1 Like

Thank you, your answer really made sense to me. Based on your answer, I can also gain a lot from other people's answers, thank you all!

1 Like

Yes, or to phrase it in terms of the compiler's view, the lifetime of the returned reference must be tied to the guard object (which necessarily lives shorter), and not to the inner value itself.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.