// Example 111: RefCell<T> Runtime Borrow Checking
//
// RefCell<T> enforces borrowing rules at runtime instead of compile time.
// Panics if you violate: multiple &mut or &mut + & simultaneously.
use std::cell::RefCell;
// Approach 1: Mutable collection through shared reference
fn approach1() {
let items: RefCell<Vec<String>> = RefCell::new(Vec::new());
items.borrow_mut().push("first".into());
items.borrow_mut().push("second".into());
items.borrow_mut().push("third".into());
let borrowed = items.borrow();
assert_eq!(borrowed.len(), 3);
println!("Items: {}", borrowed.join(", "));
}
// Approach 2: Shared mutable stack
struct Stack<T> {
data: RefCell<Vec<T>>,
}
impl<T: std::fmt::Debug> Stack<T> {
fn new() -> Self { Stack { data: RefCell::new(Vec::new()) } }
fn push(&self, val: T) { self.data.borrow_mut().push(val); }
fn pop(&self) -> Option<T> { self.data.borrow_mut().pop() }
fn peek(&self) -> Option<String> {
self.data.borrow().last().map(|v| format!("{:?}", v))
}
}
fn approach2() {
let s = Stack::new();
s.push(1);
s.push(2);
s.push(3);
assert_eq!(s.peek(), Some("3".into()));
assert_eq!(s.pop(), Some(3));
assert_eq!(s.pop(), Some(2));
println!("Remaining top: {}", s.peek().unwrap());
}
// Approach 3: Observer pattern
struct Subject {
observers: RefCell<Vec<Box<dyn Fn(&str)>>>,
}
impl Subject {
fn new() -> Self { Subject { observers: RefCell::new(Vec::new()) } }
fn add_observer(&self, f: Box<dyn Fn(&str)>) {
self.observers.borrow_mut().push(f);
}
fn notify_all(&self, msg: &str) {
for obs in self.observers.borrow().iter() {
obs(msg);
}
}
}
fn approach3() {
let log: RefCell<Vec<String>> = RefCell::new(Vec::new());
let subject = Subject::new();
// We need Rc for shared access to log from multiple closures
use std::rc::Rc;
let log = Rc::new(log);
let log1 = Rc::clone(&log);
subject.add_observer(Box::new(move |msg| {
log1.borrow_mut().push(msg.to_string());
}));
let log2 = Rc::clone(&log);
subject.add_observer(Box::new(move |msg| {
log2.borrow_mut().push(format!("copy:{}", msg));
}));
subject.notify_all("hello");
assert_eq!(log.borrow().len(), 2);
println!("Notifications: {}", log.borrow().len());
}
fn main() {
println!("=== Approach 1: Mutable Collection ===");
approach1();
println!("\n=== Approach 2: Shared Mutable Stack ===");
approach2();
println!("\n=== Approach 3: Observer Pattern ===");
approach3();
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_refcell_borrow() {
let cell = RefCell::new(vec![1, 2, 3]);
assert_eq!(cell.borrow().len(), 3);
cell.borrow_mut().push(4);
assert_eq!(cell.borrow().len(), 4);
}
#[test]
#[should_panic]
fn test_refcell_double_mut_panics() {
let cell = RefCell::new(42);
let _a = cell.borrow_mut();
let _b = cell.borrow_mut(); // PANIC: already borrowed
}
#[test]
fn test_stack() {
let s = Stack::new();
s.push(10);
s.push(20);
assert_eq!(s.pop(), Some(20));
assert_eq!(s.pop(), Some(10));
assert_eq!(s.pop(), None);
}
#[test]
fn test_try_borrow() {
let cell = RefCell::new(42);
let _r = cell.borrow();
assert!(cell.try_borrow_mut().is_err()); // can't mut borrow while shared
}
}
(* Example 111: RefCell<T> — Runtime Borrow Checking *)
(* OCaml doesn't have borrow checking. Mutable data is always accessible.
RefCell adds Rust's borrow rules at runtime for non-Copy types. *)
(* Approach 1: Mutable list inside immutable binding *)
let approach1 () =
let items = ref [] in
items := "first" :: !items;
items := "second" :: !items;
items := "third" :: !items;
let result = List.rev !items in
assert (result = ["first"; "second"; "third"]);
Printf.printf "Items: %s\n" (String.concat ", " result)
(* Approach 2: Shared mutable state *)
type 'a stack = { mutable data : 'a list }
let push s x = s.data <- x :: s.data
let pop s = match s.data with
| [] -> None
| x :: rest -> s.data <- rest; Some x
let peek s = match s.data with [] -> None | x :: _ -> Some x
let approach2 () =
let s = { data = [] } in
push s 1; push s 2; push s 3;
assert (peek s = Some 3);
assert (pop s = Some 3);
assert (pop s = Some 2);
Printf.printf "Remaining top: %d\n" (Option.get (peek s))
(* Approach 3: Observer pattern with mutable callbacks *)
type observer = { notify : string -> unit }
type subject = { mutable observers : observer list }
let add_observer subj obs = subj.observers <- obs :: subj.observers
let notify_all subj msg = List.iter (fun o -> o.notify msg) subj.observers
let approach3 () =
let log = ref [] in
let subj = { observers = [] } in
add_observer subj { notify = fun msg -> log := msg :: !log };
add_observer subj { notify = fun msg -> log := ("copy:" ^ msg) :: !log };
notify_all subj "hello";
assert (List.length !log = 2);
Printf.printf "Notifications: %d\n" (List.length !log)
let () =
approach1 ();
approach2 ();
approach3 ();
Printf.printf "✓ All tests passed\n"