← Back to Rust Course | Chapter 13: Smart Pointers & Unsafe | Lesson 3 of 7

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.

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

markup
use std::cell::RefCell;

fn main() {
    let cell = RefCell::new(5);
    *cell.borrow_mut() += 1;
    println!("{}", cell.borrow());
}

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

markup
use std::cell::RefCell;

fn main() {
    let cell = RefCell::new(String::from("hello"));
    {
        let borrowed = cell.borrow();
        println!("Reading: {}", borrowed);
    }
    cell.borrow_mut().push_str(", world");
    println!("{}", cell.borrow());
}

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

markup
use std::cell::RefCell;
use std::rc::Rc;

fn main() {
    let shared = Rc::new(RefCell::new(0));
    let clone_a = Rc::clone(&shared);
    *clone_a.borrow_mut() += 10;
    println!("Value: {}", shared.borrow());
}

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

markup
use std::cell::RefCell;

struct Cache {
    value: RefCell<Option<i32>>,
}

fn main() {
    let cache = Cache { value: RefCell::new(None) };
    *cache.value.borrow_mut() = Some(42);
    println!("{:?}", cache.value.borrow());
}
Common Mistakes
  1. Borrowing a RefCell mutably twice at the same time, which compiles fine but panics at runtime with a borrow error.
  2. Using RefCell when a plain &mut reference would work just as well, adding unnecessary runtime checking overhead.
  3. Forgetting RefCell alone doesn't allow sharing across owners -- it is usually combined with Rc for shared, mutable-looking data.
Chapter Summary
  • 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:

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.