Search code examples
rustmutabilityborrow-checkerborrowing

To temporarily hold the borrowed object


struct Parent<'a> {
    inner: Option<&'a mut Object>
}
impl<'a> Parent<'a> {
    fn new() -> Parent<'a> {
        Parent {
            inner: None,
        }
    }
    fn bind(&mut self, obj: &'a mut Object) {
        self.inner = Some(obj);
    }
    fn unbind(&mut self) {
        self.inner = None;
    }
}
struct Object {
    inner: u32,
}
fn main() {
    let mut obj = Object { inner: 0 };
    let mut parent1 = Parent::new();
    let mut parent2 = Parent::new();
    parent1.bind(&mut obj);
    parent1.unbind();
    // parent2.bind(&mut obj);
    // ^ error

}

Struct Parent field must store the Option:

struct Parent {
    inner: Option</*  magic  */>
}

It is necessary to temporarily block the possibility of working with the object in fn main(), until you call the .unbind()


Solution

  • It's impossible for the borrow checker to prove this correct at compile-time. But you can use RefCell to make sure it works at runtime and panics if you do it wrong. Instead of storing a reference to the object in the Parent object, you can store a RefMut that allows you to modify the object. The bind function takes a reference to a RefCell and will panic if the object is already borrowed.

    use std::cell::*;
    
    struct Parent<'a> {
        inner: Option<RefMut<'a, Object>>,
    }
    impl<'a> Parent<'a> {
        fn new() -> Parent<'a> {
            Parent {
                inner: None,
            }
        }
        fn bind(&mut self, obj: &'a RefCell<Object>) {
            self.inner = Some(obj.borrow_mut());
        }
        fn unbind(&mut self) {
            self.inner = None;
        }
    }
    struct Object {
        inner: u32,
    }
    fn main() {
        let obj = RefCell::new(Object { inner: 0 });
        let mut parent1 = Parent::new();
        parent1.bind(&obj);
        parent1.unbind(); // comment this line out and you get a runtime panic
        let mut parent2 = Parent::new();
        parent2.bind(&obj);
    }