Rust 58 🦀 Interior Mutability Pattern (RefCell and Cell)
Rust’s borrow checker enforces a simple rule: a value has either one mutable reference or any number of shared references, never both. The rule is checked at compile time, and it is what makes data races impossible in safe code. But the rule is sometimes too strict. A value that is shared across many owners still needs to be mutated, or a callback must modify state captured by a shared reference. The interior mutability pattern relaxes the rule by moving the borrow check from compile time to runtime. The Cell<T> and RefCell<T> types are the two implementations. Cell<T> allows mutation through a shared reference by copying or replacing the value, and RefCell<T> allows mutation by tracking the borrows at runtime and panicking on a violation. Both are single-threaded; the multithreaded counterparts are Mutex<T> and RwLock<T>.
Key point: Interior mutability does not violate Rust’s safety rules. It moves the enforcement of the rules from compile time to runtime. A RefCell<T> that is borrowed mutably while a shared borrow is active panics, and the panic is a safe failure, not undefined behavior.
Why interior mutability exists
The shared-mutation problem. A value behind a shared reference cannot be mutated through the reference. If the value must change, and the reference must stay shared, the type must provide interior mutability. The classic example is a counter shared by many components: each component holds a shared reference, and the counter changes when any of them is used.
The callback problem. A closure captures a shared reference to a value, and the value must be mutated inside the closure. The borrow checker rejects the mutation because the closure’s captures are shared. RefCell<T> moves the check to runtime, and the mutation is allowed.
The trait-method problem. A trait method takes &self and must mutate a field of the implementing struct. The method signature cannot be changed to &mut self, because the trait is shared across types. Cell<T> or RefCell<T> in the field provides the interior mutability.
The graph problem. A graph has nodes that reference each other, and the references must be updatable. The Rc<RefCell<T>> combination provides shared ownership and mutation, which is the standard pattern for a mutable graph on a single thread.
The caching problem. A struct computes a value lazily and stores it in a field. The method that returns the value takes &self, but the field must be written. OnceCell<T> or RefCell<T> provides the interior mutability.
The observer problem. A subject holds a list of observers and notifies them when something changes. The list must be mutated through a shared reference, because the subject is shared. RefCell<Vec<Observer>> is the pattern.
The thread-safety problem. Cell<T> and RefCell<T> are not thread-safe. They use non-atomic operations and are marked !Sync. The multithreaded counterparts are Mutex<T> and RwLock<T>, which use atomic operations and locking.
a. The Cell type
Cell<T> provides interior mutability for types that are Copy or that can be replaced. The methods take &self, so they work through a shared reference.
The get and set
use std::cell::Cell;
fn main() {
let c = Cell::new(5);
println!("{}", c.get()); // 5
c.set(10);
println!("{}", c.get()); // 10
}
The get method returns a copy of the value, which is why the type must be Copy. The set method replaces the value.
The replace
use std::cell::Cell;
fn main() {
let c = Cell::new(5);
let old = c.replace(10);
println!("{}", old); // 5
println!("{}", c.get()); // 10
}
The replace method swaps the value and returns the old one.
The take
use std::cell::Cell;
fn main() {
let c = Cell::new(String::from("hello"));
let value = c.take();
println!("{}", value); // hello
// c is now empty.
}
The take method replaces the value with Default::default() and returns the old value. The take method is available for types that implement Default.
The update
use std::cell::Cell;
fn main() {
let c = Cell::new(5);
c.update(|v| v + 1);
println!("{}", c.get()); // 6
}
The update method applies a function to the value and stores the result.
The interior mutability through a shared reference
use std::cell::Cell;
struct Counter {
count: Cell<u32>,
}
impl Counter {
fn increment(&self) {
self.count.set(self.count.get() + 1);
}
fn value(&self) -> u32 {
self.count.get()
}
}
fn main() {
let counter = Counter { count: Cell::new(0) };
counter.increment();
counter.increment();
println!("{}", counter.value()); // 2
}
The increment method takes &self and mutates the count field. The Cell provides the interior mutability.
The Cell limitations
Cell<T> requires T: Copy for get, and it does not provide references to the value. A Cell<Vec<T>> cannot be mutated in place; the whole vector must be replaced. The type is appropriate for small, copyable values, not for large structures.
The Cell methods
| Method | Requires | Returns |
|---|---|---|
get() | T: Copy | A copy of the value |
set(v) | — | () |
replace(v) | — | The old value |
take() | T: Default | The old value |
update(f) | — | () |
swap(other) | — | () |
b. The RefCell type
RefCell<T> provides interior mutability for any type, not just Copy types. The borrows are checked at runtime, and a violation panics.
The borrow and borrow_mut
use std::cell::RefCell;
fn main() {
let c = RefCell::new(5);
let r = c.borrow();
println!("{}", *r); // 5
drop(r);
let mut r = c.borrow_mut();
*r = 10;
drop(r);
println!("{}", c.borrow()); // 10
}
The borrow method returns a Ref<T>, which is a shared reference. The borrow_mut method returns a RefMut<T>, which is a mutable reference. The two cannot be active at the same time.
The runtime check
use std::cell::RefCell;
fn main() {
let c = RefCell::new(5);
let r1 = c.borrow();
let r2 = c.borrow(); // OK: multiple shared borrows
drop(r1);
drop(r2);
let m = c.borrow_mut();
// let m2 = c.borrow_mut(); // ❌ Panics
drop(m);
}
A second borrow_mut while a RefMut is active panics. A borrow while a RefMut is active also panics.
The try_borrow and try_borrow_mut
use std::cell::RefCell;
fn main() {
let c = RefCell::new(5);
let m = c.borrow_mut();
match c.try_borrow() {
Ok(r) => println!("{}", *r),
Err(_) => println!("Already mutably borrowed"),
}
}
The try_borrow and try_borrow_mut methods return a Result instead of panicking. They are used when the borrow may fail and the failure is handled.
The interior mutability through a shared reference
use std::cell::RefCell;
struct Counter {
count: RefCell<u32>,
}
impl Counter {
fn increment(&self) {
*self.count.borrow_mut() += 1;
}
fn value(&self) -> u32 {
*self.count.borrow()
}
}
fn main() {
let counter = Counter { count: RefCell::new(0) };
counter.increment();
counter.increment();
println!("{}", counter.value()); // 2
}
The Counter struct is shared, and the increment method mutates the count field through the RefCell. The borrow is checked at runtime.
The borrow tracking
The RefCell<T> keeps a borrow flag: zero or more shared borrows, or one mutable borrow. The flag is updated on each borrow and drop. The borrow_count method returns the current count.
let c = RefCell::new(5);
let r = c.borrow();
println!("{}", c.borrow_count()); // 1
The borrow rules
| Borrows | State |
|---|---|
| 0 | Idle |
N shared (Ref) | N readers |
1 mutable (RefMut) | 1 writer |
| Shared + mutable | Panic |
| 2 mutable | Panic |
The Ref and RefMut types
The Ref<'a, T> and RefMut<'a, T> types implement Deref and DerefMut, so they behave like references. They release the borrow when they are dropped, which is why the borrow is tied to the lifetime of the guard.
{
let r = c.borrow();
// The borrow is active here.
} // The borrow is released here.
The scope of the borrow is the scope of the guard.
The RefCell panics
The RefCell panics on a violation. The panic is a safe failure, not undefined behavior. The message describes the violation.
thread 'main' panicked at 'already borrowed: BorrowMutError'
The panic is the trade-off for the flexibility. A RefCell that is used carefully does not panic.
The RefCell and Rc
The Rc<RefCell<T>> combination provides shared mutable state on a single thread.
use std::rc::Rc;
use std::cell::RefCell;
fn main() {
let counter = Rc::new(RefCell::new(0));
let a = Rc::clone(&counter);
let b = Rc::clone(&counter);
*a.borrow_mut() += 1;
*b.borrow_mut() += 1;
println!("{}", counter.borrow()); // 2
}
The Rc provides the shared ownership, and the RefCell provides the mutation. The combination is the standard pattern for shared mutable state.
c. The interior mutability patterns
The Cell<T> and RefCell<T> types are used in a small set of recurring patterns. The patterns are the reason the types exist.
The lazy initialization
A value is computed on first access and stored for later use.
use std::cell::RefCell;
struct Lazy {
value: RefCell<Option<i32>>,
}
impl Lazy {
fn get(&self) -> i32 {
let mut v = self.value.borrow_mut();
if v.is_none() {
*v = Some(expensive_computation());
}
v.unwrap()
}
}
fn expensive_computation() -> i32 {
42
}
The get method takes &self and initializes the value on the first call. The RefCell provides the interior mutability. The OnceCell<T> and OnceLock<T> types are the specialized versions of this pattern.
The mutable counter
use std::cell::Cell;
struct ClickCounter {
clicks: Cell<u32>,
}
impl ClickCounter {
fn click(&self) {
self.clicks.set(self.clicks.get() + 1);
}
}
The counter is incremented through a shared reference. The Cell is the right choice because the value is Copy.
The observer list
use std::cell::RefCell;
struct Subject {
observers: RefCell<Vec<Box<dyn Fn()>>>,
}
impl Subject {
fn subscribe(&self, f: Box<dyn Fn()>) {
self.observers.borrow_mut().push(f);
}
fn notify(&self) {
for f in self.observers.borrow().iter() {
f();
}
}
}
The Subject holds a list of observers and mutates the list through a shared reference. The RefCell provides the mutation.
The graph node
use std::rc::Rc;
use std::cell::RefCell;
struct Node {
value: i32,
neighbors: RefCell<Vec<Rc<Node>>>,
}
fn main() {
let a = Rc::new(Node { value: 1, neighbors: RefCell::new(vec![]) });
let b = Rc::new(Node { value: 2, neighbors: RefCell::new(vec![]) });
a.neighbors.borrow_mut().push(Rc::clone(&b));
b.neighbors.borrow_mut().push(Rc::clone(&a));
}
The Rc<RefCell<T>> combination provides a mutable graph. The Rc provides the shared ownership, and the RefCell provides the mutation.
The thread-local state
The thread_local! macro and the Cell<T> combination provide per-thread state.
use std::cell::Cell;
thread_local! {
static COUNTER: Cell<u32> = Cell::new(0);
}
fn main() {
COUNTER.with(|c| c.set(c.get() + 1));
COUNTER.with(|c| println!("{}", c.get())); // 1
}
The thread_local! macro declares a variable that is unique per thread, and the Cell provides the mutation.
The memoization
use std::cell::RefCell;
use std::collections::HashMap;
struct Memo {
cache: RefCell<HashMap<i32, i32>>,
}
impl Memo {
fn compute(&self, n: i32) -> i32 {
if let Some(&v) = self.cache.borrow().get(&n) {
return v;
}
let v = n * n;
self.cache.borrow_mut().insert(n, v);
v
}
}
The Memo struct caches the results of a computation. The RefCell provides the mutation of the cache through a shared reference.
The pattern table
| Pattern | Type | Use Case |
|---|---|---|
| Lazy init | RefCell<Option<T>> | Compute once on first access |
| Counter | Cell<u32> | Increment through &self |
| Observer | RefCell<Vec<F>> | Mutable list through &self |
| Graph | Rc<RefCell<T>> | Shared mutable graph |
| Thread-local | thread_local! + Cell<T> | Per-thread state |
| Memoization | RefCell<HashMap<K, V>> | Cached computation |
Complete Example Session
// ============================================
// PART 1: THE BASIC CELL
// ============================================
use std::cell::Cell;
fn main() {
let c = Cell::new(5);
println!("{}", c.get()); // 5
c.set(10);
println!("{}", c.get()); // 10
}
// ============================================
// PART 2: THE CELL REPLACE AND TAKE
// ============================================
fn main() {
let c = Cell::new(5);
let old = c.replace(10);
println!("{}", old); // 5
let s = Cell::new(String::from("hello"));
let value = s.take();
println!("{}", value); // hello
}
// ============================================
// PART 3: THE CELL COUNTER
// ============================================
struct Counter {
count: Cell<u32>,
}
impl Counter {
fn increment(&self) {
self.count.set(self.count.get() + 1);
}
}
fn main() {
let counter = Counter { count: Cell::new(0) };
counter.increment();
counter.increment();
println!("{}", counter.count.get()); // 2
}
// ============================================
// PART 4: THE BASIC REFCELL
// ============================================
use std::cell::RefCell;
fn main() {
let c = RefCell::new(5);
let r = c.borrow();
println!("{}", *r); // 5
drop(r);
let mut m = c.borrow_mut();
*m = 10;
drop(m);
println!("{}", c.borrow()); // 10
}
// ============================================
// PART 5: THE RUNTIME PANIC
// ============================================
fn main() {
let c = RefCell::new(5);
let m = c.borrow_mut();
// let r = c.borrow(); // ❌ Panics
drop(m);
}
// ============================================
// PART 6: THE TRY_BORROW
// ============================================
fn main() {
let c = RefCell::new(5);
let m = c.borrow_mut();
match c.try_borrow() {
Ok(r) => println!("{}", *r),
Err(_) => println!("Already mutably borrowed"),
}
}
// ============================================
// PART 7: THE REFCELL COUNTER
// ============================================
struct RefCounter {
count: RefCell<u32>,
}
impl RefCounter {
fn increment(&self) {
*self.count.borrow_mut() += 1;
}
}
fn main() {
let counter = RefCounter { count: RefCell::new(0) };
counter.increment();
counter.increment();
println!("{}", counter.count.borrow()); // 2
}
// ============================================
// PART 8: THE RC<REFCELL<T>> PATTERN
// ============================================
use std::rc::Rc;
fn main() {
let counter = Rc::new(RefCell::new(0));
let a = Rc::clone(&counter);
let b = Rc::clone(&counter);
*a.borrow_mut() += 1;
*b.borrow_mut() += 1;
println!("{}", counter.borrow()); // 2
}
// ============================================
// PART 9: THE MEMOIZATION PATTERN
// ============================================
use std::collections::HashMap;
struct Memo {
cache: RefCell<HashMap<i32, i32>>,
}
impl Memo {
fn compute(&self, n: i32) -> i32 {
if let Some(&v) = self.cache.borrow().get(&n) {
return v;
}
let v = n * n;
self.cache.borrow_mut().insert(n, v);
v
}
}
fn main() {
let m = Memo { cache: RefCell::new(HashMap::new()) };
println!("{}", m.compute(4)); // 16
println!("{}", m.compute(4)); // 16 (cached)
}
// ============================================
// PART 10: THE THREAD-LOCAL PATTERN
// ============================================
thread_local! {
static COUNTER: Cell<u32> = Cell::new(0);
}
fn main() {
COUNTER.with(|c| c.set(c.get() + 1));
COUNTER.with(|c| println!("{}", c.get())); // 1
}
The ten parts showed the basic Cell, the replace and take, the Cell counter, the basic RefCell, the runtime panic, the try_borrow, the RefCell counter, the Rc<RefCell<T>> pattern, the memoization pattern, and the thread-local pattern.
Quick Reference
The Cell Methods
| Method | Requires | Returns |
|---|---|---|
get() | T: Copy | A copy of the value |
set(v) | — | () |
replace(v) | — | The old value |
take() | T: Default | The old value |
update(f) | — | () |
swap(other) | — | () |
The RefCell Methods
| Method | Returns | Panics |
|---|---|---|
borrow() | Ref<T> | On mutable borrow |
borrow_mut() | RefMut<T> | On any borrow |
try_borrow() | Result<Ref<T>, _> | No |
try_borrow_mut() | Result<RefMut<T>, _> | No |
borrow_count() | usize | No |
The Borrow Rules
| State | Allowed |
|---|---|
| No borrow | borrow or borrow_mut |
| N shared | More borrow |
| 1 mutable | Nothing |
| Shared + mutable | Panic |
The Cell vs RefCell
| Aspect | Cell<T> | RefCell<T> |
|---|---|---|
| Copy required | For get | No |
| References | No | Yes |
| Runtime check | No | Yes |
| Panics | No | Yes |
| Overhead | None | Borrow flag |
The Thread-Safe Counterparts
| Single-threaded | Multithreaded |
|---|---|
Cell<T> | AtomicUsize, AtomicBool |
RefCell<T> | Mutex<T>, RwLock<T> |
Rc<T> | Arc<T> |
Rc<RefCell<T>> | Arc<Mutex<T>> |
Best Practices
✅ Do This:
// Use Cell for Copy types.
let counter = Cell::new(0);
counter.set(counter.get() + 1); // ✅
// Use RefCell for non-Copy types.
let list = RefCell::new(vec![1, 2, 3]);
list.borrow_mut().push(4); // ✅
// Keep the borrow scope short.
{
let mut v = list.borrow_mut();
v.push(4);
} // ✅
// Use try_borrow when the failure is expected.
if let Ok(r) = cell.try_borrow() { } // ✅
// Use Rc<RefCell<T>> for shared mutable state.
let shared = Rc::new(RefCell::new(0)); // ✅
// Use the thread-safe counterparts across threads.
let shared = Arc::new(Mutex::new(0)); // ✅
// Use OnceCell for lazy initialization.
let value = OnceCell::new(); // ✅
❌ Don’t Do This:
// Don't hold a borrow across a function call.
let r = cell.borrow();
other_function(); // ❌ May panic
drop(r);
// Don't use RefCell across threads.
let c = RefCell::new(0);
thread::spawn(move || { c.borrow_mut(); }); // ❌ Not Sync
// Don't use Cell for non-Copy types.
let c = Cell::new(vec![1, 2, 3]);
c.get(); // ❌ Vec is not Copy
// Don't ignore the panic.
// A double borrow_mut is a bug. // ❌
// Don't use RefCell when a plain reference works.
fn process(v: &Vec<i32>) { } // ✅ Use a reference
Common Pitfalls
| Pitfall | Why It Happens | Fix |
|---|---|---|
| Panic on borrow | Double borrow_mut | Shorten the borrow |
Not Sync | RefCell across threads | Use Mutex |
get on non-Copy | Cell<Vec<T>> | Use RefCell |
| Borrow across call | Guard not dropped | Use a block |
| Leaked guard | mem::forget on Ref | Do not forget the guard |
| Borrow not released | Guard held too long | Drop the guard |
Real-World Examples
1. Cell Counter
struct Counter { count: Cell<u32> }
2. RefCell List
let list = RefCell::new(vec![1, 2, 3]);
3. Rc<RefCell>
let shared = Rc::new(RefCell::new(0));
4. try_borrow
if let Ok(r) = cell.try_borrow() { }
5. Lazy Init
let value = OnceCell::new();
6. Memoization
cache: RefCell<HashMap<K, V>>
7. Thread-local
thread_local! { static C: Cell<u32> = Cell::new(0); }
8. Graph Node
neighbors: RefCell<Vec<Rc<Node>>>
9. Observer
observers: RefCell<Vec<Box<dyn Fn()>>>
10. Arc<Mutex>
let data = Arc::new(Mutex::new(0));
Visual
The Cell
┌────────────────────────────────────────────────────────────┐
│ Cell<T> │
│ │
│ struct Counter { count: Cell<u32> } │
│ │
│ &Counter │
│ ┌──────────────────────────────────────────────────┐ │
│ │ count: Cell<u32> │ │
│ │ ┌─────────────────┐ │ │
│ │ │ value: 5 │ │ │
│ │ └─────────────────┘ │ │
│ │ │ │
│ │ counter.increment() takes &self │ │
│ │ self.count.set(self.count.get() + 1) │ │
│ │ │ │
│ │ The Cell allows mutation through a shared ref. │ │
│ └──────────────────────────────────────────────────┘ │
│ │
│ T must be Copy for get(). │
│ No runtime check, no panic. │
└────────────────────────────────────────────────────────────┘
The RefCell
┌────────────────────────────────────────────────────────────┐
│ RefCell<T> │
│ │
│ let c = RefCell::new(5); │
│ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ RefCell │ │
│ │ borrow_flag: 0 │ │
│ │ value: 5 │ │
│ └──────────────────────────────────────────────────┘ │
│ │
│ c.borrow() │
│ ┌──────────────────────────────────────────────────┐ │
│ │ borrow_flag: 1 │ │
│ │ Ref<T> returned │ │
│ └──────────────────────────────────────────────────┘ │
│ │
│ c.borrow_mut() while borrow is active │
│ ┌──────────────────────────────────────────────────┐ │
│ │ ❌ PANIC: already borrowed │ │
│ └──────────────────────────────────────────────────┘ │
│ │
│ The borrow flag is checked at runtime. │
└────────────────────────────────────────────────────────────┘
The Rc<RefCell> Pattern
┌────────────────────────────────────────────────────────────┐
│ Rc<RefCell<T>> PATTERN │
│ │
│ STACK HEAP │
│ ┌──────────┐ ┌──────────────────────┐ │
│ │ a: ptr ──┼──────────────────▶│ strong: 2 │ │
│ ├──────────┤ │ weak: 0 │ │
│ │ b: ptr ──┼──────────────────▶│ RefCell │ │
│ └──────────┘ │ borrow_flag: 0 │ │
│ │ value: 0 │ │
│ └──────────────────────┘ │
│ │
│ *a.borrow_mut() += 1; │
│ ┌──────────────────────────────────────────────────┐ │
│ │ a borrows the RefCell mutably, increments. │ │
│ │ The RefMut guard is dropped, the flag resets. │ │
│ └──────────────────────────────────────────────────┘ │
│ │
│ Rc: shared ownership. RefCell: interior mutability. │
└────────────────────────────────────────────────────────────┘
The Borrow Rules
┌────────────────────────────────────────────────────────────┐
│ THE BORROW RULES │
│ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ borrow_flag = 0 │ │
│ │ borrow() → flag = 1, Ref<T> │ │
│ │ borrow_mut() → flag = -1, RefMut<T> │ │
│ └──────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ borrow_flag = N (shared borrows active) │ │
│ │ borrow() → flag = N+1 │ │
│ │ borrow_mut() → ❌ PANIC │ │
│ └──────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ borrow_flag = -1 (mutable borrow active) │ │
│ │ borrow() → ❌ PANIC │ │
│ │ borrow_mut() → ❌ PANIC │ │
│ └──────────────────────────────────────────────────┘ │
│ │
│ The flag is restored when the guard is dropped. │
└────────────────────────────────────────────────────────────┘
Summary
| Item | Value |
|---|---|
| Interior mutability | Mutation through a shared reference |
Cell<T> | For Copy types; no references |
RefCell<T> | For any type; runtime borrow check |
get / set | Cell read and write |
borrow / borrow_mut | RefCell read and write |
try_borrow / try_borrow_mut | Non-panicking variants |
Rc<RefCell<T>> | Shared mutable single-threaded state |
Arc<Mutex<T>> | Shared mutable multithreaded state |
Ref<T> / RefMut<T> | The borrow guards |
OnceCell<T> | Lazy initialization |
Key takeaways:
- Interior mutability moves the borrow check from compile time to runtime. The
Cell<T>andRefCell<T>types allow mutation through a shared reference, which the borrow checker would normally reject. The runtime check is the trade-off for the flexibility. Cell<T>is forCopytypes and provides no references. Thegetmethod returns a copy, and thesetmethod replaces the value. The type has no runtime overhead beyond the value itself.RefCell<T>works with any type and provides references. Theborrowandborrow_mutmethods return guards that implementDerefandDerefMut. The borrows are tracked with a flag, and a violation panics.- The
try_borrowandtry_borrow_mutmethods do not panic. They return aResultthat the caller can handle. They are used when the borrow may fail and the failure is expected. - The
Rc<RefCell<T>>combination is the standard for shared mutable state. TheRcprovides the shared ownership, and theRefCellprovides the mutation. The combination is single-threaded. - The
Arc<Mutex<T>>combination is the multithreaded counterpart. TheArcprovides the atomic shared ownership, and theMutexprovides the thread-safe mutation. - A
RefCellpanic is a safe failure. The panic is the runtime’s response to a borrow violation, and it is not undefined behavior. The message describes the violation. - The borrow scope is the guard’s scope. A
ReforRefMutguard holds the borrow until it is dropped. Holding a guard across a function call is the most common cause of a panic. Cell<T>andRefCell<T>are not thread-safe. They are marked!Sync, and the compiler rejects sending them across threads. TheMutex<T>andRwLock<T>types are the thread-safe alternatives.
Remember: Interior mutability is the pattern that lets a value be mutated through a shared reference. The Cell<T> type is for small, Copy values and provides no references. The RefCell<T> type works with any type and provides references with runtime borrow checking. The Rc<RefCell<T>> combination is the standard for shared mutable state on a single thread, and the Arc<Mutex<T>> combination is the multithreaded counterpart. The runtime check is the trade-off: the borrow rules are enforced at runtime instead of compile time, and a violation panics. The result is the flexibility of shared mutation without the unsafety of raw pointers, and a runtime check that is predictable and well-defined.
Stop using slow, ad-bloated tool sites! 🤮
🔎 Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)
⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free
🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!