RefCell and Interior Mutability
RefCell lets you change a value even through a reference that's supposed to be read-only, but it checks the rules while your program runs instead of ahead of time.In this page:
let name = RefCell::new(value);
name.borrow();
name.borrow_mut();
let shared = Rc::new(RefCell::new(value));
Mutating Through RefCell
RefCell<T> lets you obtain a mutable reference to its inner data via .borrow_mut(), even when the RefCell itself is bound immutably, moving borrow checking to runtime.
Example: Mutating Through RefCell
// Bring `std::cell::RefCell` into scope
use std::cell::RefCell;
// Entry point: execution of the program starts here
fn main() {
// Declare an immutable variable `cell`, set to `RefCell::new(5)`
let cell = RefCell::new(5);
*cell.borrow_mut() += 1;
// Print "{}" to the console, substituting in `cell.borrow()` (with a trailing newline)
println!("{}", cell.borrow());
}
Login to try C/C++/Java/PHP code in the editor
Runtime Borrow Checking
Unlike compile-time references, RefCell checks borrowing rules while the program runs: attempting two conflicting borrows at once causes a panic rather than a compile error.
Example: Runtime Borrow Checking
// Bring `std::cell::RefCell` into scope
use std::cell::RefCell;
// Entry point: execution of the program starts here
fn main() {
// Declare an immutable variable `cell`, set to `RefCell::new(String::from("hello"))`
let cell = RefCell::new(String::from("hello"));
{
// Declare an immutable variable `borrowed`, set to `cell.borrow()`
let borrowed = cell.borrow();
// Print "Reading: {}" to the console, substituting in `borrowed` (with a trailing newline)
println!("Reading: {}", borrowed);
}
cell.borrow_mut().push_str(", world");
// Print "{}" to the console, substituting in `cell.borrow()` (with a trailing newline)
println!("{}", cell.borrow());
}
Login to try C/C++/Java/PHP code in the editor
Combining Rc and RefCell
Rc<RefCell<T>> is a common pattern that gives multiple owners shared access to the same data, while still allowing any of them to mutate it through the RefCell.
Example: Combining Rc and RefCell
// Bring `std::cell::RefCell` into scope
use std::cell::RefCell;
// Bring `std::rc::Rc` into scope
use std::rc::Rc;
// Entry point: execution of the program starts here
fn main() {
// Declare an immutable variable `shared`, set to `Rc::new(RefCell::new(0))`
let shared = Rc::new(RefCell::new(0));
// Declare an immutable variable `clone_a`, set to `Rc::clone(&shared)`
let clone_a = Rc::clone(&shared);
*clone_a.borrow_mut() += 10;
// Print "Value: {}" to the console, substituting in `shared.borrow()` (with a trailing newline)
println!("Value: {}", shared.borrow());
}
Login to try C/C++/Java/PHP code in the editor
Why Interior Mutability Exists
Interior mutability provides an escape hatch for situations where Rust's strict compile-time borrowing rules are too rigid, such as a shared cache updated from multiple places, at the cost of moving some checks to runtime.
Example: Why Interior Mutability Exists
// Bring `std::cell::RefCell` into scope
use std::cell::RefCell;
// Define a struct `Cache` to group related fields together
struct Cache {
value: RefCell<Option<i32>>,
}
// Entry point: execution of the program starts here
fn main() {
// Declare an immutable variable `cache`, set to `Cache { value: RefCell::new(None) }`
let cache = Cache { value: RefCell::new(None) };
*cache.value.borrow_mut() = Some(42);
// Print "{:?}" to the console, substituting in `cache.value.borrow()` (with a trailing newline)
println!("{:?}", cache.value.borrow());
}
Login to try C/C++/Java/PHP code in the editor
- Borrowing a
RefCellmutably twice at the same time, which compiles fine but panics at runtime with a borrow error. - Using
RefCellwhen a plain&mutreference would work just as well, adding unnecessary runtime checking overhead. - Forgetting
RefCellalone doesn't allow sharing across owners -- it is usually combined withRcfor shared, mutable-looking data.
RefCell<T>allows mutating data even through an immutable reference, moving borrow checking from compile time to runtime..borrow()and.borrow_mut()return runtime-checked references, and violating borrowing rules causes a panic instead of a compile error.Rc<RefCell<T>>is a common pattern combining shared ownership with interior mutability.- Interior mutability should be used deliberately, since misuse turns compile-time errors into runtime panics.
Chapter Quiz — Complete all 7 topics to unlock
0/7 topics done
Complete these topics first: