🦀 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

112: Cow\<T\> — Clone on Write

Difficulty: 2 Level: Intermediate `Cow<'a, T>` holds either a borrowed reference or an owned value — deferring heap allocation until the moment you actually need to modify the data.

The Problem This Solves

A common pattern: a function receives a string or slice, and in most cases returns it unchanged — but occasionally needs to modify it. The naive approach allocates a new `String` every time, even when 90% of calls don't modify anything. Wasteful. The alternative — returning a reference — doesn't work when the function sometimes needs to return new data it created. You'd need two different return types, a messy `Option`, or caller-side allocation. In C, you'd use `const char` for the borrow case and `char` for the owned case, with a flag or convention to tell the caller which it is. Leaks and double-frees follow. In Python, strings are immutable, so every "modification" already allocates a new string — the problem doesn't arise, but neither does the optimization. `Cow<'a, T>` (Clone on Write) is a smart enum: `Borrowed(&'a T)` or `Owned(T)`. You manipulate it uniformly. If you never mutate it, no allocation ever happens. The first time you call `to_mut()` on a `Borrowed` variant, it clones into an `Owned`. The allocation is deferred until — and only if — it's actually needed.

The Intuition

`Cow<T>` is "maybe I'll need to modify this, maybe I won't" — it holds a borrowed reference until the first mutation, then clones into an owned value exactly once, so you only pay for allocation when you actually need it.

How It Works in Rust

use std::borrow::Cow;

// Returns borrowed if no change needed, owned only if sanitization happens
fn sanitize(input: &str) -> Cow<str> {
 if input.chars().all(|c| c.is_alphanumeric() || c == ' ') {
     Cow::Borrowed(input)  // no allocation — return a view of the input
 } else {
     let cleaned: String = input
         .chars()
         .filter(|c| c.is_alphanumeric() || c == ' ')
         .collect();
     Cow::Owned(cleaned)   // allocate only when we actually changed something
 }
}

fn demo() {
 let clean = "hello world";
 let dirty = "hello! world@";
 
 let r1 = sanitize(clean); // Borrowed — no allocation
 let r2 = sanitize(dirty); // Owned — allocation happened
 
 println!("{}", r1); // works uniformly
 println!("{}", r2); // works uniformly
}

// to_mut() clones lazily — only on first mutation
fn demo_lazy_clone() {
 let data: Cow<[i32]> = Cow::Borrowed(&[1, 2, 3]);
 
 // No allocation yet
 println!("Length: {}", data.len()); // reads through borrow
 
 // First mutation triggers the clone
 let mut data = data;
 data.to_mut().push(4); // NOW it allocates and clones
 println!("{:?}", data); // [1, 2, 3, 4] — owned
}

// Useful in APIs that accept both &str and String
fn process(input: Cow<str>) {
 // Caller can pass &str or String — no allocation either way
 println!("Processing: {}", input);
}

fn caller() {
 process(Cow::Borrowed("literal"));          // no allocation
 process(Cow::Owned(String::from("owned"))); // already owned
}

What This Unlocks

Key Differences

ConceptOCamlRust
Borrowed vs owned string`string` (always owned/GC)`&str` (borrowed) vs `String` (owned) vs `Cow<str>` (either)
Lazy copyNot needed (GC shares)`Cow` — clone deferred to first mutation
Zero-copy read pathGC provides structural sharing`Cow::Borrowed` — zero allocation
Explicit allocation momentInvisible (GC)Visible — `to_mut()` or `Cow::Owned`
Common use caseN/ASanitizers, normalizers, functions that conditionally transform
// Example 112: Cow<T> Clone-on-Write
//
// Cow<'a, T> = either borrowed (&'a T) or owned (T).
// Clones only when mutation is needed. Efficient for "maybe modify" patterns.

use std::borrow::Cow;

// Approach 1: Conditional string modification
fn normalize_whitespace(s: &str) -> Cow<str> {
    if s.contains('\t') {
        // Need to modify → allocate (Owned)
        Cow::Owned(s.replace('\t', " "))
    } else {
        // No modification → borrow (no allocation)
        Cow::Borrowed(s)
    }
}

fn approach1() {
    let clean = "hello world";
    let dirty = "hello\tworld";
    let r1 = normalize_whitespace(clean);
    let r2 = normalize_whitespace(dirty);
    assert_eq!(&*r1, "hello world");
    assert_eq!(&*r2, "hello world");
    
    // r1 is Borrowed (zero-cost), r2 is Owned (allocated)
    println!("Clean borrowed: {}, Fixed owned: {}", 
        matches!(r1, Cow::Borrowed(_)), matches!(r2, Cow::Owned(_)));
}

// Approach 2: Default with optional override
fn with_default<'a>(default: &'a str, override_val: Option<String>) -> Cow<'a, str> {
    match override_val {
        Some(v) => Cow::Owned(v),
        None => Cow::Borrowed(default),
    }
}

fn approach2() {
    let default = "default_config";
    let r1 = with_default(default, None);
    let r2 = with_default(default, Some("custom".into()));
    assert_eq!(&*r1, "default_config");
    assert_eq!(&*r2, "custom");
    println!("r1={}, r2={}", r1, r2);
}

// Approach 3: Batch processing with Cow
fn process_items<'a>(items: &'a [&'a str]) -> Vec<Cow<'a, str>> {
    items.iter().map(|&item| {
        if item.len() > 5 {
            Cow::Owned(item.to_uppercase())
        } else {
            Cow::Borrowed(item)
        }
    }).collect()
}

fn approach3() {
    let items = vec!["hi", "hello", "extraordinary"];
    let result = process_items(&items);
    assert_eq!(&*result[0], "hi");
    assert_eq!(&*result[2], "EXTRAORDINARY");
    let strs: Vec<&str> = result.iter().map(|c| c.as_ref()).collect();
    println!("Processed: {}", strs.join(", "));
}

fn main() {
    println!("=== Approach 1: Normalize Whitespace ===");
    approach1();
    println!("\n=== Approach 2: Default with Override ===");
    approach2();
    println!("\n=== Approach 3: Batch Processing ===");
    approach3();
}

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

    #[test]
    fn test_cow_borrowed() {
        let result = normalize_whitespace("clean");
        assert!(matches!(result, Cow::Borrowed(_)));
    }

    #[test]
    fn test_cow_owned() {
        let result = normalize_whitespace("has\ttab");
        assert!(matches!(result, Cow::Owned(_)));
        assert_eq!(&*result, "has tab");
    }

    #[test]
    fn test_cow_to_mut() {
        let mut cow: Cow<str> = Cow::Borrowed("hello");
        // to_mut() clones if borrowed
        cow.to_mut().push_str(" world");
        assert_eq!(&*cow, "hello world");
        assert!(matches!(cow, Cow::Owned(_)));
    }

    #[test]
    fn test_with_default() {
        let d = with_default("def", None);
        assert!(matches!(d, Cow::Borrowed(_)));
        let o = with_default("def", Some("custom".into()));
        assert!(matches!(o, Cow::Owned(_)));
    }

    #[test]
    fn test_cow_into_owned() {
        let cow: Cow<str> = Cow::Borrowed("hello");
        let owned: String = cow.into_owned();
        assert_eq!(owned, "hello");
    }
}
(* Example 112: Cow<T> Clone-on-Write — Lazy Cloning *)

(* OCaml doesn't need CoW — GC handles sharing.
   But we can demonstrate the concept of deferred copying. *)

(* Approach 1: Conditional modification *)
let normalize_whitespace s =
  if String.contains s '\t' then
    String.map (fun c -> if c = '\t' then ' ' else c) s
  else
    s  (* no allocation if no tabs *)

let approach1 () =
  let clean = "hello world" in
  let dirty = "hello\tworld" in
  let r1 = normalize_whitespace clean in
  let r2 = normalize_whitespace dirty in
  assert (r1 = "hello world");
  assert (r2 = "hello world");
  Printf.printf "Clean: %s, Fixed: %s\n" r1 r2

(* Approach 2: Default with override *)
let with_default default_val override_opt =
  match override_opt with
  | Some v -> v
  | None -> default_val

let approach2 () =
  let default = "default_config" in
  let r1 = with_default default None in
  let r2 = with_default default (Some "custom") in
  assert (r1 = "default_config");
  assert (r2 = "custom");
  Printf.printf "r1=%s, r2=%s\n" r1 r2

(* Approach 3: Batch processing with conditional transform *)
let process_items items transform_fn =
  List.map (fun item ->
    if String.length item > 5 then transform_fn item
    else item
  ) items

let approach3 () =
  let items = ["hi"; "hello"; "extraordinary"] in
  let result = process_items items String.uppercase_ascii in
  assert (result = ["hi"; "hello"; "EXTRAORDINARY"]);
  Printf.printf "Processed: %s\n" (String.concat ", " result)

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

📊 Detailed Comparison

Comparison: Cow Clone-on-Write

Conditional Modification

OCaml:

🐪 Show OCaml equivalent
let normalize s =
if String.contains s '\t' then
 String.map (fun c -> if c = '\t' then ' ' else c) s
else s  (* returns same string — GC handles it *)

Rust:

fn normalize(s: &str) -> Cow<str> {
 if s.contains('\t') {
     Cow::Owned(s.replace('\t', " "))  // allocates
 } else {
     Cow::Borrowed(s)  // zero-cost
 }
}

Default With Override

OCaml:

🐪 Show OCaml equivalent
let with_default d = function
| Some v -> v | None -> d

Rust:

fn with_default<'a>(d: &'a str, v: Option<String>) -> Cow<'a, str> {
 match v {
     Some(v) => Cow::Owned(v),
     None => Cow::Borrowed(d),
 }
}