Bypassing static borrow checker restrictions when you have an immutable reference but need to mutate inner state.
use std::cell::RefCell;
struct Logger {
log_count: RefCell<usize>, // Mutable even through &Logger
}
impl Logger {
fn log(&self, msg: &str) {
let mut count = self.log_count.borrow_mut(); // Runtime borrow check
*count += 1;
println!("[#{}] {}", *count, msg);
}
}
fn main() {
let logger = Logger { log_count: RefCell::new(0) };
logger.log("Booting server..."); // &logger is immutable!
logger.log("Connected to database.");
}Normally in Rust, having a shared reference `&T` strictly prevents mutation. Interior mutability uses `UnsafeCell<T>` under the hood to allow controlled mutation from an immutable exterior handle.
`RefCell<T>` keeps an internal integer counter representing active loans: 0 = unborrowed, positive integer = number of active `&T` borrows, -1 = active `&mut T` borrow.
If you call `.borrow_mut()` while an active `.borrow()` exists on the same thread, `RefCell` will panic at runtime rather than allow undefined memory corruption.
Compile-time borrow checking (`&T` / `&mut T`) has 0 runtime cost. `RefCell` trades slight runtime overhead and potential panics for flexibility in graph and observer patterns.
let cell = RefCell::new(42);
let r1 = cell.borrow();
// let mut r2 = cell.borrow_mut(); // PANIC: AlreadyBorrowed