This article refers to rust book ch15 and adds its own understanding, interested parties can first look at the official documentation.
Rust has two ways to achieve mutability
- Inheritance variability: for example, if a struct is declared with let mut, then any field of the struct can be modified later.
- Internal mutability: use
RefCellto wrap a variable or field so that it can be modified even if the external variable is read-only
It seems that inheritance mutability is enough, so why do we need the so-called
interior mutability internal mutability? Let’s analyze two examples.
Cache has three fields,
sum , where
sum simulates the lazy init lazy loading mode, the above code is not working, the reason is simple, when let initialize the variable i, is immutable.
There are two ways to fix this problem, the let declaration specifies
let mut i , but for larger projects the outer variables are likely to be immutable and immutable. This is where internal mutability comes in handy.
This is the code after the fix, sum type is
Cell<Option<i32>> , beginners are easily confused, what the hell is this ah? Some also nest multiple wrappers, like
Rc<Cell<Option<i32>> and so on.
Rc represents shared ownership, but because the T in
Rc<T> is read-only and cannot be modified, we need to use
Cell to seal a layer so that ownership is shared, but still mutable, and
Option is the common value
Some(T) or the null value
None , which is still very understandable.
If you are not writing rust code and just want to read the source code to understand the process, there is no need to delve into these wrappers, focus on the real type of the wrapper can be.
The example given by the official website is Mock Objects, the code is longer, but the principle is the same.
Finally, it’s all about wrapping the structure fields with
This code is very representative, if the variable
a is not wrapped with
Cell, then it is not allowed to modify
b read-only borrow exists, as guaranteed by the rust compiler during the compile period: Given a pair, only N immutable borrowings or one mutable borrowing are allowed to exist in scope (NLL).
Cell gets and modifies values via
set, this function requires that value must implement the
Copy trait, if we replace it with another structure, the compile will report an error.
From the above, we can see that
struct Test does not implement
Copy by default, so
get is not allowed. Is there any way to get the underlying struct? You can use
get_mut to return a reference to the underlying data, but this requires the entire variable to be a
let mut, so it is not consistent with the original intent of using
Cell, we use
RefCell to get either immutable borrowing via
borrow or mutable borrowing of the underlying data via
Code from badboi.dev, compiled successfully, but failed to run.
borrow_mut to get a variable borrow, and while it is still in scope,
cell_ref_2 also tries to get a variable borrow, at which point the runtime check reports an error and panic directly.
RefCell moves the borrow rule from compile-time compile to runtime , with some runtime overhead.
This is the official example, used in combination with
RefCell to share ownership while modifying List node values.
Internal mutability provides great flexibility, but filtered to runtime overhead, still can not be abused, performance problems are not significant, the focus is the lack of compile-time static checks, will cover up many errors.