Introduction
The RefCell class in Rust provides interior mutability, a design pattern that allows you to mutate data even when there are immutable references to that data. It achieves this by enforcing Rust's borrowing rules at runtime, rather than compile time. This is particularly useful when you need to modify data that is shared across multiple parts of a program.
Using RefCell
The RefCell class can be used to create a mutable data structure that can be safely accessed and modified at runtime. Here is a basic example:
use std::cell::RefCell;
fn main() {
let data = RefCell::new(5);
{
let mut value = data.borrow_mut();
*value += 1;
}
println!("Value: {}", data.borrow());
}
// Output: Value: 6
Key Methods
Below are some of the key methods exposed by the RefCell class:
new
Creates a new RefCell containing the given value.
use std::cell::RefCell;
fn main() {
let cell = RefCell::new(5);
println!("Created RefCell with value: {}", cell.borrow());
}
// Output: Created RefCell with value: 5
borrow
Immutably borrows the wrapped value. Returns a Ref which implements Deref to allow access to the inner value.
use std::cell::RefCell;
fn main() {
let cell = RefCell::new(5);
let value = cell.borrow();
println!("Borrowed value: {}", value);
}
// Output: Borrowed value: 5
borrow_mut
Mutably borrows the wrapped value. Returns a RefMut which implements DerefMut to allow mutable access to the inner value.
use std::cell::RefCell;
fn main() {
let cell = RefCell::new(5);
{
let mut value = cell.borrow_mut();
*value += 1;
}
println!("Modified value: {}", cell.borrow());
}
// Output: Modified value: 6
replace
Replaces the wrapped value with a new one, returning the old value.
use std::cell::RefCell;
fn main() {
let cell = RefCell::new(5);
let old_value = cell.replace(10);
println!("Old value: {}, New value: {}", old_value, cell.borrow());
}
// Output: Old value: 5, New value: 10
into_inner
Consumes the RefCell, returning the wrapped value.
use std::cell::RefCell;
fn main() {
let cell = RefCell::new(5);
let value = cell.into_inner();
println!("Unwrapped value: {}", value);
}
// Output: Unwrapped value: 5
Example Usage
Example 1: Basic Usage
use std::cell::RefCell;
fn main() {
let data = RefCell::new(5);
{
let mut value = data.borrow_mut();
*value += 1;
}
println!("Value: {}", data.borrow());
}
// Output: Value: 6
Example 2: Borrowing and Modifying
use std::cell::RefCell;
fn main() {
let cell = RefCell::new(5);
{
let mut value = cell.borrow_mut();
*value += 1;
}
println!("Modified value: {}", cell.borrow());
}
// Output: Modified value: 6
Example 3: Replacing the Value
use std::cell::RefCell;
fn main() {
let cell = RefCell::new(5);
let old_value = cell.replace(10);
println!("Old value: {}, New value: {}", old_value, cell.borrow());
}
// Output: Old value: 5, New value: 10
Example 4: Consuming the RefCell
use std::cell::RefCell;
fn main() {
let cell = RefCell::new(5);
let value = cell.into_inner();
println!("Unwrapped value: {}", value);
}
// Output: Unwrapped value: 5
Example 5: Nested RefCell
use std::cell::RefCell;
fn main() {
let nested = RefCell::new(RefCell::new(5));
{
let inner = nested.borrow();
let mut value = inner.borrow_mut();
*value += 1;
}
println!("Nested value: {}", nested.borrow().borrow());
}
// Output: Nested value: 6
Considerations
RefCellenforces Rust's borrowing rules at runtime, not compile time. This can lead topanicif the rules are violated.- Using
RefCellcan be less performant than other synchronization primitives because it checks borrow rules at runtime. RefCellis not thread-safe. For thread-safe interior mutability, useMutexorRwLock.
See Also
- Cell - Similar to
RefCell, but provides interior mutability without runtime borrowing checks. - Mutex - A mutual exclusion primitive useful for protecting shared data across threads.
- RwLock - A reader-writer lock for allowing concurrent reads or exclusive writes across threads.
- Arc - An atomic reference-counted smart pointer for shared ownership across threads.
