๐Ÿฆ€ Functional Rust
๐ŸŽฌ Traits & Generics Shared behaviour, static vs dynamic dispatch, zero-cost polymorphism.
๐Ÿ“ Text version (for readers / accessibility)

โ€ข Traits define shared behaviour โ€” like interfaces but with default implementations

โ€ข Generics with trait bounds: fn process(item: T) โ€” monomorphized at compile time

โ€ข Static dispatch (impl Trait) = zero cost; dynamic dispatch (dyn Trait) = runtime flexibility via vtable

โ€ข Blanket implementations apply traits to all types matching a bound

โ€ข Associated types and supertraits enable complex type relationships

389: Newtype Pattern

Difficulty: 2 Level: Intermediate Wrap a type to give it distinct identity and semantics โ€” preventing accidental misuse at zero runtime cost.

The Problem This Solves

You have a function that takes two `f64` arguments: `fn set_position(x: f64, y: f64)`. Nothing stops a caller from accidentally swapping them: `set_position(y_coord, x_coord)`. It compiles. It runs. It's wrong. A `String` for an email address and a `String` for a username are type-identical but semantically incompatible โ€” the compiler won't catch it if you pass one where the other is expected. The newtype pattern solves this by wrapping each primitive in a distinct struct: `struct Email(String)`. Now `Email` and `Username` are different types โ€” passing one where the other is expected is a compile error. You get type-level documentation and compiler enforcement for free, with zero runtime overhead (the wrapper is erased). This is one of Rust's most practically useful patterns, and it scales from simple `struct Meters(f64)` to complex `struct UserId(Uuid)` in production systems.

The Intuition

`struct Email(String)` is a tuple struct with one field. At runtime, it's identical to `String` โ€” no extra memory, no extra indirection. At compile time, it's a completely different type. The Rust compiler cannot convert between `Email` and `Username` without an explicit `.0` field access. The pattern also lets you implement traits on the wrapper that you can't implement on the inner type (the orphan rule prevents `impl Display for String` in your crate, but `impl Display for Email` is fine). This is the standard workaround for the orphan rule.

How It Works in Rust

// Distinct types for distinct concepts
struct Email(String);
struct Username(String);
struct Meters(f64);
struct Feet(f64);

impl Email {
 fn new(s: &str) -> Result<Self, &'static str> {
     if s.contains('@') { Ok(Email(s.to_string())) }
     else { Err("invalid email") }
 }

 fn as_str(&self) -> &str { &self.0 }
}

fn send_welcome(email: &Email, name: &Username) {
 println!("Welcome {}, sending to {}", name.0, email.as_str());
}

let email = Email::new("alice@example.com").unwrap();
let name  = Username("Alice".to_string());

send_welcome(&email, &name);       // OK
// send_welcome(&name, &email);    // compile error: wrong types

// Implement traits on the wrapper (orphan rule workaround)
use std::fmt;
impl fmt::Display for Email {
 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
     write!(f, "{}", self.0)
 }
}

// Zero-cost: same memory layout as inner type
assert_eq!(std::mem::size_of::<Email>(), std::mem::size_of::<String>());
Use `#[repr(transparent)]` when FFI requires the wrapper to have identical ABI to the inner type.

What This Unlocks

Key Differences

ConceptOCamlRust
Distinct type alias`type email = string` (alias, not distinct!)`struct Email(String)` (distinct type)
Abstract type`module M : sig type t end` (fully abstract)`struct Email(String)` with private field
Zero-cost wrapperN/A โ€” types are structural in OCaml`#[repr(transparent)]` guarantees same layout
Orphan rule workaroundModule systemNewtype wrapper enables trait impls
// Newtype pattern in Rust
use std::fmt;
use std::ops::Add;

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

#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
struct Kilograms(f64);

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

impl fmt::Display for Meters {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:.2}m", self.0)
    }
}

impl fmt::Display for Kilograms {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:.2}kg", self.0)
    }
}

impl Add for Meters {
    type Output = Meters;
    fn add(self, other: Meters) -> Meters { Meters(self.0 + other.0) }
}

// Domain-specific validated type
#[derive(Debug, Clone)]
struct Email(String);

impl Email {
    fn new(s: &str) -> Option<Self> {
        if s.contains('@') && s.contains('.') {
            Some(Email(s.to_string()))
        } else {
            None
        }
    }

    fn as_str(&self) -> &str { &self.0 }
}

impl fmt::Display for Email {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.0)
    }
}

fn bmi(mass: Kilograms, height: Meters) -> f64 {
    mass.0 / (height.0 * height.0)
}

// Type safety: bmi(Meters(1.75), Kilograms(70.0)) would NOT compile!

fn main() {
    let height = Meters(1.75);
    let weight = Kilograms(70.0);
    println!("Height: {}", height);
    println!("Weight: {}", weight);
    println!("BMI: {:.1}", bmi(weight, height));

    let distance = Meters(5.0) + Meters(3.5);
    println!("Distance: {}", distance);

    match Email::new("user@example.com") {
        Some(email) => println!("Valid email: {}", email),
        None => println!("Invalid email"),
    }

    println!("Size of Meters: {} (same as f64: {})",
             std::mem::size_of::<Meters>(),
             std::mem::size_of::<f64>());
}

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

    #[test]
    fn test_newtype_safety() {
        let m = Meters(5.0);
        let k = Kilograms(70.0);
        // These are different types โ€” compiler enforces distinction
        assert_ne!(m.0, k.0 as f64); // just to use both
        assert_eq!(m + Meters(3.0), Meters(8.0));
    }

    #[test]
    fn test_email_validation() {
        assert!(Email::new("a@b.com").is_some());
        assert!(Email::new("invalid").is_none());
    }

    #[test]
    fn test_transparent_size() {
        assert_eq!(std::mem::size_of::<Meters>(), std::mem::size_of::<f64>());
    }
}
(* Newtype pattern in OCaml *)

(* Distinct types for distinct units *)
type meters = Meters of float
type kilograms = Kilograms of float
type seconds = Seconds of float

(* Domain type: email validated at construction *)
type email = Email of string

let make_email s =
  if String.contains s '@' then Some (Email s)
  else None

let string_of_meters (Meters m) = Printf.sprintf "%.2fm" m
let string_of_kg (Kilograms k) = Printf.sprintf "%.2fkg" k

(* Type safety: cannot pass meters where kg expected *)
let bmi (Kilograms mass) (Meters height) =
  mass /. (height *. height)

(* Cannot mix: bmi (Meters 1.75) (Kilograms 70.0) would be a type error *)

let () =
  let height = Meters 1.75 in
  let weight = Kilograms 70.0 in
  Printf.printf "Height: %s\n" (string_of_meters height);
  Printf.printf "Weight: %s\n" (string_of_kg weight);
  Printf.printf "BMI: %.1f\n" (bmi weight height);
  match make_email "user@example.com" with
  | Some (Email e) -> Printf.printf "Valid email: %s\n" e
  | None -> Printf.printf "Invalid email\n"