Rustlers Atom 2.9: RefCell/Cell interior mutability
April 18, 2026•346 words
In rust you cannot mutate data behind a shared (immutable) reference. But what if you need to? What if you have a struct that is logically immutable to the outside world, but needs to update an internal counter, a cache, or a log?
This pattern is called Interior Mutability. Rust provides safe wrappers that let you bend the rules by moving the checks from compile time to runtime.
Cell<T>: Copying in and out
Cell is for types that implement Copy (like integers or booleans). It doesn’t give you a reference to the inner data. Instead, it lets you copy the value out or swap a new value in, even via an immutable reference.
use std::cell::Cell;
struct Sensor{
id: String,
counter: Cell<u8>,
}
impl Sensor{
fn increment(s:Self){
let mut current = s.counter.get();
current += 1;
s.counter.set(current);
}
}
fn main() {
let s = Sensor{
id: String::from("SENSOR"),
counter: Cell::new(0),
};
s.increment();
println!("{} counter is {}", s.id, s.counter.get());
}
Cell is safe because it never hands out pointers to its insides; it just moves values in and out.
RefCell<T>: Dynamic Borrowing
If you need references to non-Copy data (like a String or Vec), use RefCell, a struct that tracks borrows at runtime. It has an internal counter:
borrow(): Increments the reader count. Returns aRef<T>.borrow_mut(): Checks that reader count is 0 and writer count is 0. Returns aRefMut<T>.
If you violate the borrowing rules (e.g., call borrow_mut() while a borrow is active), the program will panic and crash.
use std::cell::RefCell;
struct SuperSensor{
id: String,
values: RefCell<Vec<i32>>,
}
impl SuperSensor{
fn read(& self){
let mut v = self.values.borrow_mut();
v.push(1);
}
}
fn main() {
let s = SuperSensor{
id: String::from("SENSOR"),
values: RefCell::new(Vec::new()),
};
s.read();
s.read();
s.read();
println!("{} counter is {:?}", s.id, s.values.borrow());
}
Use RefCell sparingly. It imposes a small runtime performance penalty and moves safety checks to runtime, where failures are crashes rather than compiler errors.