// Example 110: Cell<T> Interior Mutability
//
// Cell<T> allows mutation through a shared reference (&T).
// Works only with Copy types. No runtime borrow checking overhead.
use std::cell::Cell;
// Approach 1: Simple mutable counter
fn approach1() {
let counter = Cell::new(0);
counter.set(counter.get() + 1);
counter.set(counter.get() + 1);
assert_eq!(counter.get(), 2);
println!("Counter: {}", counter.get());
}
// Approach 2: Mutable field in an "immutable" struct
struct Config {
name: String,
call_count: Cell<u32>, // interior mutability
}
fn use_config(c: &Config) {
c.call_count.set(c.call_count.get() + 1);
println!("Config '{}' used {} times", c.name, c.call_count.get());
}
fn approach2() {
let c = Config { name: "default".into(), call_count: Cell::new(0) };
use_config(&c);
use_config(&c);
assert_eq!(c.call_count.get(), 2);
}
// Approach 3: Caching with Cell<Option<T>> for Copy types
struct Lazy<F: Fn() -> i32> {
compute: F,
cache: Cell<Option<i32>>,
}
impl<F: Fn() -> i32> Lazy<F> {
fn new(compute: F) -> Self {
Lazy { compute, cache: Cell::new(None) }
}
fn get(&self) -> i32 {
match self.cache.get() {
Some(v) => v,
None => {
let v = (self.compute)();
self.cache.set(Some(v));
v
}
}
}
}
fn approach3() {
let calls = Cell::new(0);
let expensive = Lazy::new(|| {
calls.set(calls.get() + 1);
42
});
let v1 = expensive.get();
let v2 = expensive.get();
assert_eq!(v1, 42);
assert_eq!(v2, 42);
assert_eq!(calls.get(), 1);
println!("Computed once: {} (calls: {})", v1, calls.get());
}
fn main() {
println!("=== Approach 1: Cell Counter ===");
approach1();
println!("\n=== Approach 2: Interior Mutability in Struct ===");
approach2();
println!("\n=== Approach 3: Lazy Caching ===");
approach3();
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cell_basic() {
let c = Cell::new(10);
c.set(20);
assert_eq!(c.get(), 20);
}
#[test]
fn test_cell_in_shared_ref() {
let c = Cell::new(0);
let r1 = &c;
let r2 = &c;
r1.set(1);
r2.set(r2.get() + 1);
assert_eq!(c.get(), 2);
}
#[test]
fn test_config_call_count() {
let c = Config { name: "test".into(), call_count: Cell::new(0) };
use_config(&c);
use_config(&c);
use_config(&c);
assert_eq!(c.call_count.get(), 3);
}
#[test]
fn test_lazy_computes_once() {
let count = Cell::new(0);
let lazy = Lazy::new(|| { count.set(count.get() + 1); 99 });
assert_eq!(lazy.get(), 99);
assert_eq!(lazy.get(), 99);
assert_eq!(count.get(), 1);
}
}
(* Example 110: Cell<T> Interior Mutability — OCaml ref → Rust Cell *)
(* Approach 1: Simple mutable counter with ref *)
let approach1 () =
let counter = ref 0 in
counter := !counter + 1;
counter := !counter + 1;
assert (!counter = 2);
Printf.printf "Counter: %d\n" !counter
(* Approach 2: Mutable field in an immutable struct *)
type config = { name : string; mutable call_count : int }
let use_config c =
c.call_count <- c.call_count + 1;
Printf.printf "Config '%s' used %d times\n" c.name c.call_count
let approach2 () =
let c = { name = "default"; call_count = 0 } in
use_config c;
use_config c;
assert (c.call_count = 2)
(* Approach 3: Caching/memoization with ref *)
let lazy_value init =
let cache = ref None in
fun () ->
match !cache with
| Some v -> v
| None ->
let v = init () in
cache := Some v;
v
let approach3 () =
let calls = ref 0 in
let expensive = lazy_value (fun () -> incr calls; 42) in
let v1 = expensive () in
let v2 = expensive () in
assert (v1 = 42);
assert (v2 = 42);
assert (!calls = 1);
Printf.printf "Computed once: %d (calls: %d)\n" v1 !calls
let () =
approach1 ();
approach2 ();
approach3 ();
Printf.printf "✓ All tests passed\n"