๐Ÿฆ€ Functional Rust
๐ŸŽฌ Pattern Matching Exhaustive match, destructuring, guards, let-else โ€” compiler-verified branching.
๐Ÿ“ Text version (for readers / accessibility)

โ€ข match is exhaustive โ€” the compiler ensures every variant is handled

โ€ข Destructuring extracts fields from structs, tuples, and enums in one step

โ€ข Guards (if conditions) add extra logic to match arms

โ€ข if let and let-else provide concise single-pattern matching

โ€ข Nested patterns and @ bindings handle complex data shapes elegantly

572: ref Patterns and &

Difficulty: 2 Level: Beginner Understand how `ref`, `&`, and match ergonomics interact โ€” borrow inside patterns without fighting the borrow checker.

The Problem This Solves

You have a `Vec<i32>` and you want to sum it. `values.iter()` gives you `&i32` references, and your closure gets `&&i32` โ€” a reference to a reference. Adding `&&i32` values doesn't compile directly. You need to strip the outer reference. More commonly: you match on a `Vec<String>`, destructure the first element to a `String`, and suddenly the whole vector is partially moved. You only wanted to look at it. The confusion deepens because there are three overlapping tools for this: the `ref` keyword in patterns, the `&` pattern that dereferences, and match ergonomics (automatic `ref` insertion when matching a reference). They all exist for good historical and ergonomic reasons, and they all interact.

The Intuition

When you match `&value` against `&T`, you're asking Rust to "open the reference" and give you what's inside. `let &x = &5` gives you `x: i32` โ€” the dereference happens in the pattern. When you use `ref x` in a pattern, you're telling Rust: "bind `x` as a reference to the matched value, don't move it." `let ref x = s` is equivalent to `let x = &s`. Modern Rust has match ergonomics: when you match a `&T` value against a non-reference pattern, Rust inserts the `ref` automatically. `if let Some(s) = &opt` gives you `s: &String` โ€” no explicit `ref` needed. This covers 90% of cases. The cases where you still reach for `ref` explicitly: `let` bindings in non-reference contexts, or when you want clarity about what's happening.

How It Works in Rust

// & in closure pattern โ€” strip one reference from iter()
let values = vec![1, 2, 3, 4, 5];
let sum: i32 = values.iter().map(|&x| x).sum();
// values.iter() yields &i32; |&x| destructures it to i32

// ref in let binding โ€” explicit borrow without moving
let s = String::from("hello");
let ref r = s;   // r: &String; s still owned
println!("r={} s={}", r, s);

// Match ergonomics โ€” matching &Option<String> auto-borrows
let opt = Some(String::from("hello"));
if let Some(s) = &opt {  // s: &String โ€” ref inserted automatically
 println!("borrowed: {}", s);
}
println!("opt still alive: {:?}", opt);  // not moved

// ref in slice pattern โ€” explicit when needed
fn first_two_borrowed(v: &[String]) -> Option<(&str, &str)> {
 match v {
     [ref a, ref b, ..] => Some((a, b)),  // borrow elements, not move
     _                  => None,
 }
}

// ref mut โ€” borrow first element mutably
fn increment_first(v: &mut [i32]) {
 if let [ref mut first, ..] = v {
     *first += 1;
 }
}

What This Unlocks

Key Differences

ConceptOCamlRust
Borrows in patternsN/A (GC, no ownership)`ref x` borrows; `&x` dereferences
Mutable reference cell`let x = ref v` (explicit cell)`ref mut x` in pattern
Automatic borrowingN/AMatch ergonomics: `&T` against non-ref pattern inserts `ref`
Iteration referencesN/A`iter()` yields `&T`; use `&x` or `.copied()`
`ref` in letN/A`let ref x = value` โ‰ก `let x = &value`
fn demo_deref_pattern() {
    let values = vec![1,2,3,4,5];
    // &x strips one layer of reference โ€” x is i32
    let sum: i32 = values.iter().map(|&x| x).sum();
    println!("sum = {}", sum);
}

fn first_two_borrowed(v: &[String]) -> Option<(&str, &str)> {
    match v {
        [ref a, ref b, ..] => Some((a, b)),
        _                  => None,
    }
}

fn increment_first(v: &mut [i32]) {
    if let [ref mut first, ..] = v { *first += 1; }
}

fn demo_ref_in_let() {
    let s = String::from("hello");
    let ref r = s;   // r: &String, s still owned
    println!("r={} s={}", r, s);
}

fn main() {
    demo_deref_pattern();
    demo_ref_in_let();

    let words: Vec<String> = ["a","b","c"].iter().map(|s|s.to_string()).collect();
    if let Some((a,b)) = first_two_borrowed(&words) {
        println!("first two: {}, {}", a, b);
    }

    let mut nums = vec![10,20,30];
    increment_first(&mut nums);
    println!("{:?}", nums);

    // Match ergonomics: auto-ref on &Option
    let opt = Some(String::from("hello"));
    if let Some(s) = &opt {   // s: &String, opt still usable
        println!("borrowed: {}", s);
    }
    println!("opt: {:?}", opt);
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test] fn test_inc() { let mut v=vec![5,6]; increment_first(&mut v); assert_eq!(v[0],6); }
    #[test] fn test_deref() {
        let v = vec![1i32,2,3];
        let s: i32 = v.iter().map(|&x| x).sum();
        assert_eq!(s, 6);
    }
}
(* OCaml โ€” GC refs are explicit cells, not borrowing annotations *)
let () =
  let x = ref 42 in
  x := 100;
  Printf.printf "x=%d\n" !x;

  let opts = [Some 1; None; Some 42] in
  List.iter (function
    | Some v -> Printf.printf "Some %d\n" v
    | None   -> Printf.printf "None\n"
  ) opts