๐Ÿฆ€ Functional Rust

1046: Clone-on-Write Collections

Difficulty: Intermediate Category: Data Structures Concept: Defer cloning until mutation with `Cow<'_, [T]>` and `Cow<'_, str>` Key Insight: `Cow` (Clone-on-Write) borrows data for reads and only clones when mutation is needed โ€” OCaml's immutable data structures achieve this naturally through structural sharing, making explicit Cow unnecessary.
// 1046: Clone-on-Write: Cow<'_, [T]> for Read-Mostly Data
// Avoid cloning until mutation is actually needed

use std::borrow::Cow;

/// Process data without cloning if no modification needed
fn process_data(data: &[i32], threshold: i32) -> Cow<'_, [i32]> {
    if data.iter().all(|&x| x <= threshold) {
        // No change needed โ€” borrow original data
        Cow::Borrowed(data)
    } else {
        // Need to modify โ€” clone and filter
        Cow::Owned(data.iter().map(|&x| x.min(threshold)).collect())
    }
}

fn cow_borrow_vs_owned() {
    let data = vec![1, 2, 3, 4, 5];

    // Case 1: All values within threshold โ€” no clone
    let result = process_data(&data, 10);
    assert!(matches!(result, Cow::Borrowed(_)));
    assert_eq!(&*result, &[1, 2, 3, 4, 5]);

    // Case 2: Some values exceed threshold โ€” clones and caps
    let result = process_data(&data, 3);
    assert!(matches!(result, Cow::Owned(_)));
    assert_eq!(&*result, &[1, 2, 3, 3, 3]);
}

/// Cow<str> for string processing
fn normalize_name(name: &str) -> Cow<'_, str> {
    if name.contains(char::is_uppercase) {
        // Need to modify โ€” allocate new string
        Cow::Owned(name.to_lowercase())
    } else {
        // Already lowercase โ€” just borrow
        Cow::Borrowed(name)
    }
}

fn cow_str_demo() {
    let name1 = "alice";
    let result1 = normalize_name(name1);
    assert!(matches!(result1, Cow::Borrowed(_)));
    assert_eq!(&*result1, "alice");

    let name2 = "Alice";
    let result2 = normalize_name(name2);
    assert!(matches!(result2, Cow::Owned(_)));
    assert_eq!(&*result2, "alice");
}

/// to_mut() triggers clone only on first mutation
fn to_mut_demo() {
    let data = vec![1, 2, 3, 4, 5];
    let mut cow: Cow<'_, [i32]> = Cow::Borrowed(&data);

    // Reading doesn't clone
    assert_eq!(cow[0], 1);
    assert!(matches!(cow, Cow::Borrowed(_)));

    // Mutation triggers clone
    cow.to_mut()[0] = 99;
    assert!(matches!(cow, Cow::Owned(_)));
    assert_eq!(cow[0], 99);

    // Second mutation doesn't clone again
    cow.to_mut()[1] = 88;
    assert_eq!(&*cow, &[99, 88, 3, 4, 5]);

    // Original unchanged
    assert_eq!(data, vec![1, 2, 3, 4, 5]);
}

/// Cow in function signatures โ€” accept both owned and borrowed
fn print_items(items: Cow<'_, [i32]>) -> usize {
    items.len()
}

fn flexible_api() {
    let owned = vec![1, 2, 3];
    let borrowed = &[4, 5, 6][..];

    assert_eq!(print_items(Cow::Owned(owned)), 3);
    assert_eq!(print_items(Cow::Borrowed(borrowed)), 3);
}

fn main() {
    cow_borrow_vs_owned();
    cow_str_demo();
    to_mut_demo();
    flexible_api();
    println!("โœ“ All tests passed");
}

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

    #[test]
    fn test_cow_borrow() { cow_borrow_vs_owned(); }

    #[test]
    fn test_cow_str() { cow_str_demo(); }

    #[test]
    fn test_to_mut() { to_mut_demo(); }

    #[test]
    fn test_flexible() { flexible_api(); }

    #[test]
    fn test_into_owned() {
        let data = vec![1, 2, 3];
        let cow: Cow<'_, [i32]> = Cow::Borrowed(&data);
        let owned: Vec<i32> = cow.into_owned();
        assert_eq!(owned, vec![1, 2, 3]);
    }
}
(* 1046: Clone-on-Write for Collections *)
(* OCaml's immutable data structures are inherently "copy-on-write" *)
(* via structural sharing โ€” no explicit Cow needed *)

(* Approach 1: OCaml's natural structural sharing *)
let structural_sharing () =
  let original = [1; 2; 3; 4; 5] in
  (* Prepending shares the tail โ€” no copy needed *)
  let extended = 0 :: original in
  assert (extended = [0; 1; 2; 3; 4; 5]);
  assert (original = [1; 2; 3; 4; 5]);  (* unchanged *)
  (* Both lists share the [1;2;3;4;5] tail in memory *)
  ()

(* Approach 2: Explicit copy-on-write with ref *)
type 'a cow = {
  mutable data: 'a array;
  mutable is_owned: bool;
}

let cow_borrow arr = { data = arr; is_owned = false }

let cow_to_owned cow =
  if cow.is_owned then cow
  else { data = Array.copy cow.data; is_owned = true }

let cow_modify cow idx value =
  let cow = cow_to_owned cow in
  cow.data.(idx) <- value;
  cow

let cow_demo () =
  let shared = [|1; 2; 3; 4; 5|] in
  let c1 = cow_borrow shared in
  let c2 = cow_borrow shared in
  assert (not c1.is_owned);
  assert (not c2.is_owned);
  (* Modify c1 โ€” triggers copy *)
  let c1 = cow_modify c1 0 99 in
  assert (c1.is_owned);
  assert (c1.data.(0) = 99);
  (* c2 and shared are unaffected *)
  assert (c2.data.(0) = 1);
  assert (shared.(0) = 1)

(* Approach 3: Functional update pattern (natural CoW) *)
let functional_update () =
  let data = [1; 2; 3; 4; 5] in
  (* "Modify" by creating new structure *)
  let modified = List.map (fun x -> if x = 3 then 99 else x) data in
  assert (data = [1; 2; 3; 4; 5]);    (* original unchanged *)
  assert (modified = [1; 2; 99; 4; 5])

let () =
  structural_sharing ();
  cow_demo ();
  functional_update ();
  Printf.printf "โœ“ All tests passed\n"

๐Ÿ“Š Detailed Comparison

Clone-on-Write Collections โ€” Comparison

Core Insight

Clone-on-write defers allocation until mutation occurs. In Rust, this is explicit via `Cow<'a, T>`. In OCaml, immutable data structures with structural sharing provide this behavior implicitly โ€” you never clone because you never mutate.

OCaml Approach

  • Immutable data = natural CoW via structural sharing
  • `0 :: list` shares the tail โ€” zero-copy extension
  • `List.map` creates new structure only where changed
  • No explicit Cow type needed
  • GC handles shared references transparently
  • Explicit CoW possible with `ref` + `Array.copy` but rarely needed

Rust Approach

  • `Cow<'a, [T]>`: enum of `Borrowed(&'a [T])` and `Owned(Vec<T>)`
  • `Cow<'a, str>`: `Borrowed(&'a str)` or `Owned(String)`
  • `to_mut()`: clones on first mutation, returns `&mut`
  • `into_owned()`: consumes Cow, returns owned value
  • Functions can return `Cow` to avoid cloning on happy path
  • Deref coercion makes reading transparent

Comparison Table

FeatureOCamlRust (`Cow`)
CoW mechanismImplicit (immutability)Explicit (`Cow<'a, T>`)
Read costZero (shared)Zero (Deref)
Write costNew allocation (structural sharing)Clone on first write
API complexityNone (just use values)`Cow::Borrowed`/`Owned`, `to_mut()`
Return from functionValue (shared by GC)`Cow` (avoids clone if unmodified)
Typical useDefault behaviorOptimization for read-heavy paths