๐Ÿฆ€ Functional Rust
๐ŸŽฌ Rust Ownership in 30 seconds Visual walkthrough of ownership, moves, and automatic memory management.
๐Ÿ“ Text version (for readers / accessibility)

โ€ข Each value in Rust has exactly one owner โ€” when the owner goes out of scope, the value is dropped

โ€ข Assignment moves ownership by default; the original binding becomes invalid

โ€ข Borrowing (&T / &mut T) lets you reference data without taking ownership

โ€ข The compiler enforces: many shared references OR one mutable reference, never both

โ€ข No garbage collector needed โ€” memory is freed deterministically at scope exit

104: Mutable References (&mut T)

Difficulty: 2 Level: Intermediate Exactly one writer can hold a mutable reference at a time โ€” and while it exists, no readers are allowed โ€” eliminating data races at compile time.

The Problem This Solves

Data races are among the hardest bugs to debug. In C or Java, two threads can simultaneously read and write the same memory. The result depends on timing โ€” it might work 99% of the time and fail mysteriously under load. Mutexes help, but they're manually applied: forget one, and the race is back. Valgrind and ThreadSanitizer catch some of these at runtime, but only if that code path runs during testing. Even single-threaded code suffers from aliasing bugs. In C, you can have two pointers to the same memory location โ€” one through a struct field, one through a direct pointer โ€” and modifying through one invalidates assumptions you made through the other. The C standard calls this "undefined behavior." Rust calls it "a compile error." Rust's mutable reference rule โ€” "one writer, zero readers" โ€” is a compile-time proof that no two pieces of code can observe an inconsistent state in memory. There's only ever one mutable reference at a time, and while it exists, no one else can read or write the value. Data races become structurally impossible.

The Intuition

A mutable reference (`&mut T`) is an exclusive lock checked at compile time: exactly one piece of code can write to a value at a time, and while it's writing, no readers exist โ€” making data races and aliasing bugs impossible by construction.

How It Works in Rust

fn append_exclamation(s: &mut String) {
 s.push_str("!"); // exclusive write access โ€” safe
}

fn demo_exclusive_borrow() {
 let mut text = String::from("hello");
 append_exclamation(&mut text);
 println!("{}", text); // "hello!"
}

// ERROR: can't have two mutable references at once
fn broken() {
 let mut x = 5;
 let r1 = &mut x;
 let r2 = &mut x; // ERROR: cannot borrow `x` as mutable more than once
 println!("{} {}", r1, r2);
}

// ERROR: can't mix shared and mutable references
fn also_broken() {
 let mut x = 5;
 let r1 = &x;      // shared borrow
 let r2 = &mut x;  // ERROR: cannot borrow `x` as mutable
                   // because it is also borrowed as immutable
 println!("{} {}", r1, r2);
}

// FIX: use references in separate scopes
fn fixed() {
 let mut x = 5;
 {
     let r1 = &mut x;
     *r1 += 1; // exclusive write
 } // r1 dropped here
 let r2 = &x; // now a shared borrow is fine
 println!("{}", r2); // 6
}

fn modify_vec(v: &mut Vec<i32>) {
 v.push(42);        // fine โ€” exclusive access
 v.retain(|&n| n > 0); // also fine
}

What This Unlocks

Key Differences

ConceptOCamlRust
Mutable variables`ref` cells, mutable record fields`let mut` + `&mut T`
Multiple writersPossible (runtime races in threads)Compile error โ€” only one `&mut T`
Read while writingPossibleCompile error โ€” `&T` and `&mut T` can't coexist
Data race preventionManual (Mutex, careful code)Structural โ€” compiler enforces it
Exclusivity enforcementNone (GC manages)Compile time, zero runtime cost
// Example 104: Mutable References (&mut T)
//
// Only ONE &mut T at a time. No &T while &mut T exists.
// This prevents data races at compile time.

// Approach 1: Mutable reference to a struct
struct Counter {
    count: i32,
}

fn increment(c: &mut Counter) {
    c.count += 1;
}

fn get_count(c: &Counter) -> i32 {
    c.count
}

fn approach1() {
    let mut c = Counter { count: 0 };
    increment(&mut c);
    increment(&mut c);
    increment(&mut c);
    assert_eq!(get_count(&c), 3);
    println!("Counter: {}", get_count(&c));
}

// Approach 2: Mutable reference for accumulation
fn sum_into(data: &[i32], total: &mut i32) {
    for &x in data {
        *total += x;
    }
}

fn approach2() {
    let mut total = 0;
    sum_into(&[1, 2, 3, 4, 5], &mut total);
    assert_eq!(total, 15);
    println!("Total: {}", total);
}

// Approach 3: In-place mutation of slices
fn reverse_in_place(arr: &mut [i32]) {
    let n = arr.len();
    for i in 0..n / 2 {
        arr.swap(i, n - 1 - i);
    }
}

fn approach3() {
    let mut arr = vec![1, 2, 3, 4, 5];
    reverse_in_place(&mut arr);
    assert_eq!(arr, vec![5, 4, 3, 2, 1]);
    println!("Reversed: {:?}", arr);
}

fn main() {
    println!("=== Approach 1: Mutable Struct Reference ===");
    approach1();
    println!("\n=== Approach 2: Accumulation ===");
    approach2();
    println!("\n=== Approach 3: In-Place Mutation ===");
    approach3();
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_increment() {
        let mut c = Counter { count: 0 };
        increment(&mut c);
        assert_eq!(c.count, 1);
    }

    #[test]
    fn test_exclusive_access() {
        let mut val = 10;
        let r = &mut val;
        *r += 5;
        // Can't create another &mut or & while r exists
        assert_eq!(*r, 15);
    }

    #[test]
    fn test_sum_into() {
        let mut total = 0;
        sum_into(&[10, 20, 30], &mut total);
        assert_eq!(total, 60);
    }

    #[test]
    fn test_reverse() {
        let mut v = vec![1, 2, 3];
        reverse_in_place(&mut v);
        assert_eq!(v, vec![3, 2, 1]);
    }

    #[test]
    fn test_mut_ref_reborrow() {
        let mut data = vec![1, 2, 3];
        {
            let r = &mut data;
            r.push(4);
        } // r dropped here
        // Now we can borrow again
        assert_eq!(data.len(), 4);
    }
}
(* Example 104: Mutable References โ€” OCaml Mutable Fields โ†’ Rust &mut *)

(* Approach 1: Mutable record fields *)
type counter = { mutable count : int }

let increment c = c.count <- c.count + 1
let get_count c = c.count

let approach1 () =
  let c = { count = 0 } in
  increment c;
  increment c;
  increment c;
  assert (get_count c = 3);
  Printf.printf "Counter: %d\n" (get_count c)

(* Approach 2: Ref cells โ€” mutable references *)
let approach2 () =
  let total = ref 0 in
  List.iter (fun x -> total := !total + x) [1; 2; 3; 4; 5];
  assert (!total = 15);
  Printf.printf "Total: %d\n" !total

(* Approach 3: Mutable arrays *)
let reverse_in_place arr =
  let n = Array.length arr in
  for i = 0 to n / 2 - 1 do
    let tmp = arr.(i) in
    arr.(i) <- arr.(n - 1 - i);
    arr.(n - 1 - i) <- tmp
  done

let approach3 () =
  let arr = [| 1; 2; 3; 4; 5 |] in
  reverse_in_place arr;
  assert (arr = [| 5; 4; 3; 2; 1 |]);
  Printf.printf "Reversed: %s\n"
    (String.concat ", " (Array.to_list (Array.map string_of_int arr)))

let () =
  approach1 ();
  approach2 ();
  approach3 ();
  Printf.printf "โœ“ All tests passed\n"

๐Ÿ“Š Detailed Comparison

Comparison: Mutable References

Mutating a Counter

OCaml:

๐Ÿช Show OCaml equivalent
type counter = { mutable count : int }
let increment c = c.count <- c.count + 1
let c = { count = 0 } in
increment c; increment c;
Printf.printf "%d\n" c.count  (* 2 *)

Rust:

struct Counter { count: i32 }
fn increment(c: &mut Counter) { c.count += 1; }
let mut c = Counter { count: 0 };
increment(&mut c);
increment(&mut c);
println!("{}", c.count); // 2

Accumulating a Sum

OCaml:

๐Ÿช Show OCaml equivalent
let total = ref 0 in
List.iter (fun x -> total := !total + x) [1; 2; 3];
Printf.printf "%d\n" !total

Rust:

let mut total = 0;
for &x in &[1, 2, 3] {
 total += x;  // total is mutably borrowed implicitly
}
println!("{}", total);

In-Place Array Reversal

OCaml:

๐Ÿช Show OCaml equivalent
let reverse_in_place arr =
let n = Array.length arr in
for i = 0 to n / 2 - 1 do
 let tmp = arr.(i) in
 arr.(i) <- arr.(n-1-i);
 arr.(n-1-i) <- tmp
done

Rust:

fn reverse_in_place(arr: &mut [i32]) {
 let n = arr.len();
 for i in 0..n / 2 {
     arr.swap(i, n - 1 - i);
 }
}