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

591: Builder Pattern (Functional Style)

Difficulty: 3 Level: Intermediate Build complex values through method chains that consume and return `Self` โ€” immutable, composable, and type-safe configuration.

The Problem This Solves

Complex structs often have many optional fields with sensible defaults. The naive solution is a constructor with 12 parameters โ€” callers must know the order, can't skip arguments, and the call site is unreadable. The classic OOP fix is a mutable builder, but mutable builders can't be shared, partially applied, or stored as a "template" to derive variants from. The functional builder solves both problems. Each `with_*` method takes `self` by value, modifies one field, and returns `Self`. Because every step is a value transformation, you can store a base configuration and derive specializations from it. Tests can build a "default good config" and override one field per test case without mutation. The pattern extends to type-state builders with phantom types, where `build()` only compiles when all required fields have been set โ€” turning runtime panics into compile errors. The compiler enforces that you've configured everything mandatory.

The Intuition

A functional builder is a series of value transformations: each `with_*` returns a new (or consumed) `Self` with one field changed, and `build()` produces the final value โ€” the key difference from a mutable builder is composability: you can branch from any intermediate state. The trade-off: each step may clone/move the struct, which is negligible for configs but matters for large types.

How It Works in Rust

#[derive(Default, Clone)]
struct ServerConfig {
 host: String,
 port: u16,
 max_connections: usize,
 tls: bool,
}

impl ServerConfig {
 fn new() -> Self { Self::default() }

 // Each method consumes self and returns Self โ€” no mutation visible to caller
 fn with_host(mut self, host: &str) -> Self {
     self.host = host.to_string();
     self  // return moved self
 }
 fn with_port(mut self, port: u16) -> Self { self.port = port; self }
 fn with_max_connections(mut self, n: usize) -> Self { self.max_connections = n; self }
 fn with_tls(mut self) -> Self { self.tls = true; self }

 fn build(self) -> Result<Server, &'static str> {
     if self.host.is_empty() { return Err("host required"); }
     Ok(Server { config: self })
 }
}

// Base config โ€” stored as template
let base = ServerConfig::new()
 .with_host("localhost")
 .with_max_connections(100);

// Derive two variants without mutation
let dev  = base.clone().with_port(8080);
let prod = base.clone().with_port(443).with_tls();

What This Unlocks

Key Differences

ConceptOCamlRust
Chaining style`\>` pipe operator`.method()` chains on `Self`
Immutable stepNew record each step`self`-consuming returns new `Self`
Mutable stepExplicit `ref``mut self` internally โ€” immutable externally
Validation`Result`-wrapped `build``build() -> Result<T, E>`
DefaultsExplicit record literal`Default` trait + `#[derive(Default)]`
Type-stateGADTs / phantom typesPhantom type parameters on builder struct
#[derive(Debug,Clone)]
struct Config {
    host:    String,
    port:    u16,
    timeout: f64,
    retries: u32,
    tls:     bool,
}

impl Default for Config {
    fn default() -> Self {
        Config { host:"localhost".into(), port:80, timeout:30.0, retries:3, tls:false }
    }
}

// Consuming builder (functional style)
impl Config {
    fn host(mut self, h: impl Into<String>) -> Self { self.host = h.into(); self }
    fn port(mut self, p: u16)               -> Self { self.port = p; self }
    fn timeout(mut self, t: f64)             -> Self { self.timeout = t; self }
    fn retries(mut self, r: u32)             -> Self { self.retries = r; self }
    fn tls(mut self, b: bool)                -> Self { self.tls = b; self }

    fn build(self) -> Result<Self, String> {
        if self.host.is_empty() { return Err("host required".into()); }
        if self.port == 0       { return Err("port required".into()); }
        Ok(self)
    }
}

fn main() {
    let cfg = Config::default()
        .host("api.example.com")
        .port(443)
        .tls(true)
        .timeout(60.0)
        .build()
        .unwrap();
    println!("{:?}", cfg);

    // Reuse a partial config
    let base = Config::default().retries(5);
    let dev  = base.clone().host("dev.local");
    let prod = base.host("prod.example.com").tls(true);
    println!("dev:{}  prod:{}", dev.host, prod.host);

    // Validation
    let bad = Config::default().host("").port(0).build();
    println!("bad: {}", bad.unwrap_err());
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test] fn build_ok() {
        let c = Config::default().host("h").port(80).build();
        assert!(c.is_ok());
    }
    #[test] fn build_fail() {
        let c = Config::default().host("").build();
        assert!(c.is_err());
    }
}
(* Functional builder via record update in OCaml *)
type config = { host:string; port:int; timeout:float; retries:int; tls:bool }

let default_config = { host="localhost"; port=80; timeout=30.0; retries=3; tls=false }

let with_host    h c = { c with host=h }
let with_port    p c = { c with port=p }
let with_timeout t c = { c with timeout=t }
let with_tls     b c = { c with tls=b }

let () =
  let cfg =
    default_config
    |> with_host "api.example.com"
    |> with_port 443
    |> with_tls  true
    |> with_timeout 60.0
  in
  Printf.printf "host=%s port=%d tls=%b\n" cfg.host cfg.port cfg.tls;
  (* Reuse base *)
  let dev  = default_config |> with_host "dev.local" in
  let prod = default_config |> with_host "prod.example.com" |> with_tls true in
  Printf.printf "dev=%s prod=%s\n" dev.host prod.host