Rust Guide > Documentation > Collections > RefCell

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 to panic 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, use Mutex or RwLock.

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.