๐Ÿฆ€ 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

565: Tuple Struct Patterns

Difficulty: 2 Level: Beginner Destructure newtype and multi-field tuple structs in patterns โ€” get type safety without accessor boilerplate.

The Problem This Solves

Primitive obsession is the bug where your function takes three `f64` arguments โ€” distance, time, and speed โ€” and nothing stops you from passing them in the wrong order. At the call site they're all just `f64`. The compiler can't help you. Tests catch it only if you write them right. Tuple structs are the lightweight fix. `Meters(f64)` and `Seconds(f64)` are distinct types. `speed(seconds, meters)` won't compile. You pay almost nothing: no field names, just a wrapper type. But to use the inner value, you have to destructure โ€” and that's where patterns shine. The same technique applies whenever you want branded primitives: `UserId(u64)`, `Rgb(u8, u8, u8)`, `NonEmpty(String)`. You define one type, one pattern, and you're done.

The Intuition

A tuple struct is just a struct with positional fields โ€” `struct Rgb(u8, u8, u8)` is like `struct Rgb { 0: u8, 1: u8, 2: u8 }` without the field names. The pattern mirrors the constructor: if you built it with `Rgb(255, 0, 0)`, you unpack it with `Rgb(r, g, b)`. This is OCaml's single-constructor type โ€” `type meters = Meters of float` โ€” used as a newtype wrapper. The Rust version is slightly more ergonomic because it works directly in function parameter position. The pattern `fn add(Meters(a): Meters, Meters(b): Meters)` reads as: "this function takes something shaped like `Meters(a)` and `Meters(b)`." The destructuring happens right at the boundary. No intermediate variables, no `.0` accessor noise.

How It Works in Rust

#[derive(Debug, Clone, Copy, PartialEq)]
struct Meters(f64);

#[derive(Debug, Clone, Copy, PartialEq)]
struct Seconds(f64);

#[derive(Debug, Clone, Copy)]
struct Rgb(u8, u8, u8);

// Destructure in function parameter โ€” can't mix up types
fn add(Meters(a): Meters, Meters(b): Meters) -> Meters {
 Meters(a + b)
}

fn speed(Meters(d): Meters, Seconds(t): Seconds) -> f64 {
 d / t  // d and t are plain f64 โ€” safe to combine here
}

// Multi-field tuple struct
fn to_gray(Rgb(r, g, b): Rgb) -> Rgb {
 let avg = ((r as u16 + g as u16 + b as u16) / 3) as u8;
 Rgb(avg, avg, avg)
}

// Destructure in let binding
let Meters(total) = add(Meters(100.0), Meters(50.0));
println!("{:.1} m", total);  // 150.0 m

// Destructure in match
match color {
 Rgb(255, 0, 0) => "pure red",
 Rgb(r, g, b) if r == g && g == b => "gray",
 _ => "other",
}

What This Unlocks

Key Differences

ConceptOCamlRust
Definition`type meters = Meters of float``struct Meters(f64);`
Constructor`Meters 3.0``Meters(3.0)`
Destructure in `let``let Meters n = x in ...``let Meters(n) = x;`
Destructure in param`let f (Meters n) = ...``fn f(Meters(n): Meters)`
Multi-field`type rgb = RGB of int int int``struct Rgb(u8, u8, u8)`
#[derive(Debug,Clone,Copy,PartialEq)]
struct Meters(f64);
#[derive(Debug,Clone,Copy,PartialEq)]
struct Seconds(f64);
#[derive(Debug,Clone,Copy,PartialEq)]
struct Rgb(u8, u8, u8);

fn add(Meters(a): Meters, Meters(b): Meters) -> Meters { Meters(a + b) }
fn speed(Meters(d): Meters, Seconds(t): Seconds) -> f64 { d / t }

fn to_gray(Rgb(r,g,b): Rgb) -> Rgb {
    let avg = ((r as u16 + g as u16 + b as u16) / 3) as u8;
    Rgb(avg, avg, avg)
}

fn show_rgb(Rgb(r,g,b): Rgb) -> String { format!("rgb({},{},{})", r, g, b) }

fn main() {
    let Meters(total) = add(Meters(100.0), Meters(50.0));
    println!("{:.1} m", total);
    println!("{:.1} m/s", speed(Meters(200.0), Seconds(10.0)));
    println!("{}", show_rgb(to_gray(Rgb(255,0,0))));
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test] fn test_add() { assert_eq!(add(Meters(3.0),Meters(4.0)), Meters(7.0)); }
    #[test] fn test_gray() {
        let Rgb(r,g,b) = to_gray(Rgb(60,120,180));
        assert_eq!(r, g); assert_eq!(g, b);
    }
}
(* Single-constructor types in OCaml *)
type meters  = Meters  of float
type seconds = Seconds of float
type rgb     = RGB     of int * int * int

let add_m (Meters a) (Meters b) = Meters (a +. b)
let speed (Meters d) (Seconds t) = d /. t
let to_gray (RGB(r,g,b)) = let avg=(r+g+b)/3 in RGB(avg,avg,avg)
let show_rgb (RGB(r,g,b)) = Printf.sprintf "rgb(%d,%d,%d)" r g b

let () =
  let Meters total = add_m (Meters 100.) (Meters 50.) in
  Printf.printf "%.1f m\n" total;
  Printf.printf "%.1f m/s\n" (speed (Meters 200.) (Seconds 10.));
  Printf.printf "%s\n" (show_rgb (to_gray (RGB(255,0,0))))