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

436: Deriving Traits for Newtypes

Difficulty: 3 Level: Advanced Propagate trait implementations through newtype wrappers automatically โ€” so your `UserId(u64)` behaves like a `u64` where you want it to, while staying distinct where you don't.

The Problem This Solves

The newtype pattern โ€” `struct Meters(f64)` โ€” gives you type safety: you can't accidentally pass `Seconds` where `Meters` is expected. But it comes with a cost: all the traits the inner type implements (`Display`, `Add`, `Hash`, `Ord`, `PartialEq`) are now missing from the wrapper. You have to re-implement or delegate each one manually. For a simple newtype with five or six useful traits, that's fifty lines of boilerplate. Macro-generated delegation solves this. A derive macro (or `macro_rules!`) inspects the newtype's inner type and generates forwarding implementations: `impl Display for Meters` that calls `self.0.fmt(f)`, `impl Add for Meters` that calls `self.0 + other.0`, and so on. The `derive_more` crate in the ecosystem provides `#[derive(Add, Display, From, Into)]` exactly for this purpose. Understanding how to build this yourself teaches you both the newtype pattern's ergonomics and the macro technique for generating delegating impls.

The Intuition

A newtype derive macro generates `impl Trait for Wrapper` that delegates directly to the inner field โ€” so your newtype gets the trait without the boilerplate.

How It Works in Rust

// Manual newtype delegation โ€” what macros generate
struct Meters(f64);

impl std::fmt::Display for Meters {
 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
     write!(f, "{}m", self.0)  // delegate to inner, add context
 }
}

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

impl From<f64> for Meters {
 fn from(v: f64) -> Self { Meters(v) }
}

// With derive_more crate (what the proc macro does):
use derive_more::{Add, Display, From, Into};

#[derive(Debug, Clone, Copy, Add, Display, From, Into)]
#[display(fmt = "{}m", _0)]
struct Meters(f64);

// Now:
let a = Meters(1.5);
let b = Meters(2.0);
let c = a + b;          // Add works
println!("{}", c);      // Display works: "3.5m"
let raw: f64 = c.into();// Into<f64> works

// DIY macro for delegation
macro_rules! newtype_display {
 ($name:ident) => {
     impl std::fmt::Display for $name {
         fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
             self.0.fmt(f)
         }
     }
 };
}

newtype_display!(Meters);
1. Identify which traits you want to delegate to the inner type. 2. Each delegation is `self.0.method()` โ€” just forwarding through the wrapper. 3. Use `derive_more` for common traits (`Add`, `Display`, `From`, `Into`, `Deref`). 4. Write custom `macro_rules!` or proc macros for project-specific delegation patterns.

What This Unlocks

Key Differences

ConceptOCamlRust
Newtype pattern`type meters = Meters of float``struct Meters(f64)`
Automatic delegationModule sharing / functor application`derive_more`, manual `impl`, or custom macro
Type alias (no safety)`type meters = float``type Meters = f64` (alias, not newtype)
Operator overloadingNot supported`impl Add for Meters`
Display formatting`Printf` format functions`impl Display` โ€” `{}` in format strings
// Deriving traits for newtypes in Rust
use std::fmt;
use std::ops::{Add, Sub, Mul, Deref, DerefMut};
use std::cmp::Ordering;

// Macro that generates common newtype impls
macro_rules! newtype {
    // Numeric newtype with arithmetic and display
    (numeric $name:ident($inner:ty) with unit $unit:expr) => {
        #[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
        struct $name($inner);

        impl $name {
            fn new(v: $inner) -> Self { $name(v) }
            fn value(self) -> $inner { self.0 }
        }

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

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

        impl Sub for $name {
            type Output = Self;
            fn sub(self, other: Self) -> Self { $name(self.0 - other.0) }
        }

        impl Mul<$inner> for $name {
            type Output = Self;
            fn mul(self, s: $inner) -> Self { $name(self.0 * s) }
        }

        impl PartialEq<$inner> for $name {
            fn eq(&self, other: &$inner) -> bool { self.0 == *other }
        }
    };

    // Transparent wrapper with Deref
    (wrapper $name:ident($inner:ty)) => {
        #[derive(Debug, Clone, PartialEq)]
        struct $name($inner);

        impl $name {
            fn new(v: $inner) -> Self { $name(v) }
            fn into_inner(self) -> $inner { self.0 }
        }

        impl Deref for $name {
            type Target = $inner;
            fn deref(&self) -> &$inner { &self.0 }
        }

        impl DerefMut for $name {
            fn deref_mut(&mut self) -> &mut $inner { &mut self.0 }
        }

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

        impl From<$inner> for $name {
            fn from(v: $inner) -> Self { $name(v) }
        }

        impl From<$name> for $inner {
            fn from(w: $name) -> $inner { w.0 }
        }
    };
}

// Generate newtypes
newtype!(numeric Meters(f64) with unit "m");
newtype!(numeric Kilograms(f64) with unit "kg");
newtype!(numeric Seconds(f64) with unit "s");
newtype!(wrapper Email(String));
newtype!(wrapper NonEmptyVec(Vec<i32>));

fn main() {
    let d1 = Meters::new(5.0);
    let d2 = Meters::new(3.0);
    println!("{} + {} = {}", d1, d2, d1 + d2);
    println!("{} - {} = {}", d1, d2, d1 - d2);
    println!("{} * 2 = {}", d1, d1 * 2.0);
    println!("d1 > d2: {}", d1 > d2);

    let w = Kilograms::new(70.0);
    let h = Meters::new(1.75);
    println!("
BMI: {:.1}", w.value() / (h.value() * h.value()));

    // Email wrapper
    let email = Email::new("user@example.com".to_string());
    println!("
Email: {}", email);
    println!("Contains @: {}", email.contains('@'));  // Deref to String
    let raw: String = email.into_inner();
    println!("Raw string: {}", raw);

    // NonEmptyVec
    let mut v = NonEmptyVec::new(vec![1, 2, 3]);
    v.push(4);  // DerefMut allows Vec methods
    println!("Vec: {:?}", *v);
}

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

    #[test]
    fn test_meters_arithmetic() {
        let a = Meters::new(5.0);
        let b = Meters::new(3.0);
        assert_eq!((a + b).value(), 8.0);
        assert_eq!((a - b).value(), 2.0);
    }

    #[test]
    fn test_email_deref() {
        let e = Email::new("test@test.com".to_string());
        assert!(e.contains('@'));
        assert!(e.ends_with(".com"));
    }
}
(* Newtype derive in OCaml *)

(* Phantom type wrapper with all needed operations *)
type meters = Meters of float

(* Manually implement all operations *)
let add_m (Meters a) (Meters b) = Meters (a +. b)
let sub_m (Meters a) (Meters b) = Meters (a -. b)
let mul_m (Meters a) s = Meters (a *. s)
let cmp_m (Meters a) (Meters b) = compare a b
let show_m (Meters a) = Printf.sprintf "%.2fm" a
let float_of_m (Meters a) = a

(* Simulate derive for newtypes *)
module Meters = struct
  type t = Meters of float
  let create v = Meters v
  let value (Meters v) = v
  let add (Meters a) (Meters b) = Meters (a +. b)
  let sub (Meters a) (Meters b) = Meters (a -. b)
  let to_string (Meters v) = Printf.sprintf "%.2fm" v
  let compare (Meters a) (Meters b) = compare a b
end

let () =
  let d1 = Meters.create 5.0 in
  let d2 = Meters.create 3.0 in
  Printf.printf "d1 = %s\n" (Meters.to_string d1);
  Printf.printf "d1 + d2 = %s\n" (Meters.to_string (Meters.add d1 d2));
  Printf.printf "d1 > d2: %b\n" (Meters.compare d1 d2 > 0)