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
RefCell
enforces Rust's borrowing rules at runtime, not compile time. This can lead topanic
if the rules are violated.- Using
RefCell
can be less performant than other synchronization primitives because it checks borrow rules at runtime. RefCell
is not thread-safe. For thread-safe interior mutability, useMutex
orRwLock
.
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.