Search code examples
rustborrow-checkerrefcell

Assignment to a borrowed in RefCell


The following code gives me the "Assignment to borrowed a" error. Hows the compiler able to know that? Is the compiler special casing RefCell or is there something in the language that allows it to tell the compiler you have a borrowed value?

use std::cell::RefCell;

fn main() {
    let mut a = RefCell::new(A{a:5});
    let mut b = a.borrow_mut();
    a = RefCell::new(A{a:6});
}

Also, why does this code work which seems to be doing the exact same thing?

use std::cell::RefCell;

fn main() {
    let mut a = Box::new(A{a:5});
    let mut b = &mut a;
    a = Box::new(A{a:6});
}

Solution

  • The compiler is not special casing RefCell. But borrow_mut() has the following signature:

    pub fn borrow_mut(&self) -> RefMut<'_, T>
    

    So it returns a RefMut that keeps the RefCell borrowed while it is alive (because of the '_, that according to the lifetime elision rules borrows from self). So while it is alive, you cannot assign to the RefCell because it is borrowed.

    The reason the second case with a mutable reference works is that since mutable references don't implement Drop (a more precise term is that they don't have a drop glue, that is, neither they implement Drop nor any of their fields (which is none for mutable references) has a drop glue, recursively), the compiler shortens the borrow and drop the reference early. But it cannot do that with the RefMut from the RefCell because it implements Drop, and thus early-dropping it would change program behavior.