RefCell & Cell
Cell<T> for Copy values without borrow tracking. RefCell<T> runtime-enforces & / &mut rules for non-Copy data.
Recipe
use std::cell::Cell;
fn main() {
let c = Cell::new(0);
c.set(c.get() + 1);
println!("{}", c.get());
}When to reach for this: Mutate through shared handle in single-threaded code when borrow checker blocks.
Working Example
use std::cell::RefCell;
struct Cache {
hits: RefCell<u32>,
}
impl Cache {
fn hit(&self) {
*self.hits.borrow_mut() += 1;
}
fn hits(&self) -> u32 {
*self.hits.borrow()
}
}
fn main() {
let c = Cache { hits: RefCell::new(0) };
c.hit();
println!("{}", c.hits());
}What this demonstrates:
&selfmethods mutating interior viaRefCellborrow/borrow_mutruntime checks- Panic on double
borrow_mut
Deep Dive
try_borrow for non-panicking. Ref/RefMut guards deref to inner. Not Sync - single thread.
Gotchas
- Panic on reentrant borrow - Runtime borrow conflict. Fix: Shorter borrows, restructure.
- RefCell across threads - Not
Sync. Fix:Mutex. - Cell for non-Copy - Use
RefCellorUnsafeCell(unsafe). - Holding RefMut too long - Blocks other borrows. Fix: Scope limits.
- RefCell as first choice - Try compile-time borrows first.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
Mutex | Threads | Single-threaded |
Atomic* | Integer flags | Complex types |
Split fields &mut | Compile-time ok | Shared handle required |
FAQs
Cell get set?
Copy only.replace take?
RefCell::replace swaps value.as_ptr unsafe?
Advanced escape hatch.map splitted?
RefCell map borrow parts separately - careful API.try_borrow_mut?
Returns Result on conflict.RefCell in Rc?
Common pattern.lint?
refcell in async caution.Cell bool?
Runtime flags without RefCell overhead.UnsafeCell?
Unsafe interior - building blocks.test panic borrow?
should_panic double mut.Related
Stack versions: This page was written for Rust 1.97.0 (edition 2024), Tokio 1.x, Axum 0.8, serde 1.0, sqlx 0.8, clap 4, and Polars 0.46+.