Error while trying to borrow 2 fields from a struct wrapped in RefCell

前端 未结 1 807
醉话见心
醉话见心 2020-11-28 16:01

I have a struct which contains both data and a writer which will eventually be used to write the data. The struct is wrapped in a RefCell. Here\'s a small repro

相关标签:
1条回答
  • 2020-11-28 16:22

    You can manually invoke DerefMut and then save the resulting reference:

    fn write(s: RefCell<S>) {
        let mut mut_s = s.borrow_mut();
        let mut tmp = &mut *mut_s; // Here
        let str = &tmp.data.string;
        tmp.writer.write(str.as_bytes());
    }
    

    Or in one line:

    fn write(s: RefCell<S>) {
        let mut_s = &mut *s.borrow_mut(); // Here
        let str = &mut_s.data.string;
        mut_s.writer.write(str.as_bytes());
    }
    

    The problem is that borrow_mut doesn't return your struct directly — it returns a RefMut. Normally, this is transparent as this struct implements Deref and DerefMut, so any methods called on it are passed to the underlying type. The pseudo-expanded code looks something like this:

    use std::cell::RefMut;
    use std::ops::{Deref, DerefMut};
    
    fn write(s: RefCell<S>) {
        let mut mut_s: RefMut<S> = s.borrow_mut();
        let str = &Deref::deref(&mut_s).data.string;
        DerefMut::deref_mut(&mut mut_s).writer.write(str.as_bytes());
    }
    

    Rust doesn't track field-level borrows across function calls (even for Deref::deref or DerefMut::deref_mut). This causes your error, as the deref_mut method would need to be called during the outstanding borrow from the previous Deref::deref.

    The expanded version with the explicit borrow looks like this, with a single call to Deref::deref_mut:

    use std::cell::RefMut;
    use std::ops::DerefMut;
    
    fn write(s: RefCell<S>) {
        let mut mut_s: RefMut<S> = s.borrow_mut();
        let tmp: &mut S = DerefMut::deref_mut(&mut mut_s);
        let str = &tmp.data.string;
        tmp.writer.write(str.as_bytes());
    }
    

    The compiler can then track that the two borrows from that temporary value are disjoint.


    Note that this problem isn't unique to RefCell! Any type that implements DerefMut can experience the same problem. Here's some from the standard library:

    • Box
    • MutexGuard (from Mutex)
    • PeekMut (from BinaryHeap)
    • RwLockWriteGuard (from RwLock)
    • String
    • Vec
    • Pin
    0 讨论(0)
提交回复
热议问题