Hi, I am currently learning Rust from the official book, and I am having a hard time trying to understand how the interior mutability pattern works using RefCell in chapter 15.5 of the book.
Can someone explain to me in simple words so that I get a better understanding..
This is an example I wrote...
use std::cell::RefCell;
fn main() {
let val1 = RefCell::new(Vec::new());
let mut borrow_1 = val1.borrow_mut(); // The borrow_mut method returns a RefMut<T> smart pointer
borrow_1.push(10);
// val1.borrow(); // RUNTIME ERROR ! thread 'main' panicked at 'already mutably borrowed: BorrowError'
// let mut borrow_2 = val1.borrow_mut();
// borrow_2.push(12); // RUNTIME ERROR ! thread 'main' panicked at 'already borrowed: BorrowMutError'
}
My question here is why does borrow_1.push(10);
work?
From what I understand borrow_mut()
returns a RefMut<T>
smart pointer but how can I call push()
on it, I need to get the internal vector from the RefMut wrapper before calling push(),
Why does directly calling push()
on RufMut<T>
work?
What will happen if I do this (*borrow_1).push(10);
?
Next, I also tried the Cons List example in the book.
In order, to understand each line of the code I wrote some comments in the code.
Can someone review the comments I have written in this below code and verify if my understanding was correct, especially the HOW IT WORKS
part that I wrote, was this correct?
#[derive(Debug)]
enum List {
Cons(Rc<RefCell<i32>>, Rc<List>), // Our 'List' Enum now holds its data as a Rc<RefCell<i32>> and the rest of the 'List' as a Rc<List>
Nil,
}
use std::cell::RefCell;
use std::rc::Rc;
fn main() {
let value = Rc::new(RefCell::new(5)); // Wrap the value '5' in a RefCell<T> and then wrap it in a Rc<T>
// Here we use Rc::clone() to so both 'a' and 'value' have ownership of the inner 5 value
// rather than transferring ownership from 'value' to 'a' or having a borrow from value.
// Next, we wrap the `List` again in a Rc<T> since we later wish to give ownership of 'a' to 'b' and 'c'
let a = Rc::new(List::Cons(Rc::clone(&value), Rc::new(List::Nil)));
// Add a new value '3' and '4' to 'b' and 'c' respectively using `Rc::new(RefCell::new())`
// Also, adding both 'b' and 'c' as the owners of 'a' by `Rc::clone(&a)`
let b = List::Cons(Rc::new(RefCell::new(3)), Rc::clone(&a));
let c = List::Cons(Rc::new(RefCell::new(4)), Rc::clone(&a));
// At this point Rc<T> for '5' is owned by 2 owners 'value' and 'a'
// and Rc<T> for 'a' is owned by 3 owners 'a', 'b' and 'c'
// Here calling borrow_mut on 'value', uses the automatic dereferencing to dereference the Rc<T> to the inner RefCell<T>
// Then borrow_mut method on RefCell<T> returns a RefMut<T> smart pointer
// Now we use the dereference operator(*) on it and change the inner value.
// HOW IT WORKS:-
// *value.borrow_mut() -> *(Rc<RefCell<T>>.borrow_mut()) ->
// Now automatic dereferencing happens here since borrow_mut() is for RefCell<T> so it automatically deferences Rc<RefCell<T>> to RefCell<T> then ...
// -> *(RefCell<T>.borrow_mut()) -> *(RefMut<T>) -> 5 (since RefMut implements DerefMut)
*value.borrow_mut() += 10;
println!("a after = {:?}", a); // a after = Cons(RefCell { value: 15 }, Nil)
println!("b after = {:?}", b); // b after = Cons(RefCell { value: 3 }, Cons(RefCell { value: 15 }, Nil))
println!("c after = {:?}", c); // c after = Cons(RefCell { value: 4 }, Cons(RefCell { value: 15 }, Nil))
}
Thanks to anyone who takes the time to read through all this, any advice will be helpful to me.