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

118: Deref Coercions

Difficulty: 2 Level: Intermediate The compiler auto-dereferences smart pointers and string types so you rarely need to convert manually.

The Problem This Solves

Rust has multiple string types (`String`, `str`, `Box<String>`), multiple slice types (`Vec<T>`, `[T]`, `Box<Vec<T>>`), and various smart pointers (`Box<T>`, `Arc<T>`, `Rc<T>`). Writing conversion code every time you pass these to a function would be exhausting and noisy. The solution is `Deref` coercions. When you pass `&String` where `&str` is expected, the compiler automatically inserts a call to `String::deref()` and produces `&str`. This happens transitively: `&Box<String>` becomes `&String` which becomes `&str` โ€” a two-step chain the compiler figures out for you. Without this, every function that accepts string data would have to choose one representation, forcing callers to convert. With it, you write `fn greet(name: &str)` and callers can pass `&String`, `&Box<String>`, or `&str` freely. Same for slices: write `fn sum(data: &[i32])` and callers pass `&Vec<i32>` or `&[i32]` without thinking about it.

The Intuition

When you pass `&SomeSmartPointer` where `&InnerType` is expected, the compiler silently calls `.deref()` as many times as needed to make the types match.

How It Works in Rust

fn greet(name: &str) {          // expects &str
 println!("Hello, {}!", name);
}

fn sum(data: &[i32]) -> i32 {   // expects &[i32]
 data.iter().sum()
}

let owned: String = "World".to_string();
greet(&owned);                  // &String โ†’ &str (one deref step)

let boxed: Box<String> = Box::new("Box".to_string());
greet(&boxed);                  // &Box<String> โ†’ &String โ†’ &str (two steps!)

let v = vec![1, 2, 3];
sum(&v);                        // &Vec<i32> โ†’ &[i32]

// You can also implement Deref for your own wrapper types:
use std::ops::Deref;

struct Wrapper<T>(T);

impl<T> Deref for Wrapper<T> {
 type Target = T;
 fn deref(&self) -> &T { &self.0 }
}

let w = Wrapper(String::from("wrapped"));
greet(&w);   // &Wrapper<String> โ†’ &String โ†’ &str (two steps again)

What This Unlocks

Key Differences

ConceptOCamlRust
Auto type conversionAlmost none (`:>` for polymorphic variants only)Via `Deref` trait โ€” transitively
String typesOne (`string`)`String` (owned) and `&str` (borrowed slice)
Slice typesArrays and `Bigarray``Vec<T>` (owned) and `&[T]` (borrowed slice)
Custom coercionsNo mechanismImplement `Deref`
// Example 118: Deref Coercions
//
// Rust automatically converts &Box<T> โ†’ &T, &String โ†’ &str, &Vec<T> โ†’ &[T]
// via the Deref trait. This makes smart pointers ergonomic.

use std::ops::Deref;

// Approach 1: Automatic deref in function calls
fn greet(name: &str) {
    println!("Hello, {}!", name);
}

fn sum(data: &[i32]) -> i32 {
    data.iter().sum()
}

fn approach1() {
    let owned = String::from("World");
    greet(&owned);        // &String โ†’ &str (deref coercion)
    
    let boxed = Box::new(String::from("Box"));
    greet(&boxed);        // &Box<String> โ†’ &String โ†’ &str (double deref!)
    
    let v = vec![1, 2, 3];
    let total = sum(&v);  // &Vec<i32> โ†’ &[i32]
    assert_eq!(total, 6);
    println!("Sum: {}", total);
}

// Approach 2: Method resolution through Deref
fn approach2() {
    let s = Box::new(String::from("hello"));
    // Box<String> derefs to String, which derefs to str
    assert_eq!(s.len(), 5);           // calls str::len via deref chain
    assert!(s.starts_with("hel"));     // calls str::starts_with
    assert_eq!(s.to_uppercase(), "HELLO"); // calls str::to_uppercase
    println!("Box<String> methods work: len={}", s.len());
}

// Approach 3: Custom Deref implementation
struct Wrapper<T>(T);

impl<T> Deref for Wrapper<T> {
    type Target = T;
    fn deref(&self) -> &T {
        &self.0
    }
}

fn print_number(n: &i32) {
    println!("Number: {}", n);
}

fn approach3() {
    let w = Wrapper(42);
    print_number(&w);    // &Wrapper<i32> โ†’ &i32 via Deref
    assert_eq!(*w, 42);  // explicit deref
    
    let ws = Wrapper(String::from("wrapped"));
    greet(&ws);           // &Wrapper<String> โ†’ &String โ†’ &str
    println!("Custom deref works!");
}

fn main() {
    println!("=== Approach 1: Auto Deref in Calls ===");
    approach1();
    println!("\n=== Approach 2: Method Resolution ===");
    approach2();
    println!("\n=== Approach 3: Custom Deref ===");
    approach3();
}

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

    #[test]
    fn test_string_to_str() {
        let s = String::from("hello");
        let r: &str = &s; // deref coercion
        assert_eq!(r, "hello");
    }

    #[test]
    fn test_box_deref() {
        let b = Box::new(42);
        let r: &i32 = &b;
        assert_eq!(*r, 42);
    }

    #[test]
    fn test_vec_to_slice() {
        let v = vec![1, 2, 3];
        let s: &[i32] = &v;
        assert_eq!(s, &[1, 2, 3]);
    }

    #[test]
    fn test_custom_deref() {
        let w = Wrapper(String::from("test"));
        assert_eq!(w.len(), 4); // calls String::len via Deref
    }

    #[test]
    fn test_deref_chain() {
        let b: Box<String> = Box::new("hello".into());
        let s: &str = &b; // Box<String> โ†’ String โ†’ str
        assert_eq!(s, "hello");
    }
}
(* Example 118: Deref Coercions *)

(* OCaml doesn't have deref coercions โ€” types don't automatically convert.
   But we can show analogous patterns. *)

(* Approach 1: String and bytes โ€” no auto-conversion *)
let approach1 () =
  let s = "hello" in
  let bytes = Bytes.of_string s in
  (* Must explicitly convert back *)
  let s2 = Bytes.to_string bytes in
  assert (s = s2);
  Printf.printf "String: %s, Bytes length: %d\n" s (Bytes.length bytes)

(* Approach 2: Coercion via :> for subtypes *)
type animal = [`Dog | `Cat | `Bird]
type pet = [`Dog | `Cat]

let describe_animal : animal -> string = function
  | `Dog -> "dog"
  | `Cat -> "cat"
  | `Bird -> "bird"

let approach2 () =
  let my_pet : pet = `Dog in
  let description = describe_animal (my_pet :> animal) in
  assert (description = "dog");
  Printf.printf "Pet: %s\n" description

(* Approach 3: Explicit wrapping/unwrapping *)
module Wrapper : sig
  type t
  val create : int -> t
  val get : t -> int
  val map : (int -> int) -> t -> t
end = struct
  type t = int
  let create x = x
  let get x = x
  let map f x = f x
end

let approach3 () =
  let w = Wrapper.create 42 in
  let v = Wrapper.get w in
  let w2 = Wrapper.map (fun x -> x + 1) w in
  assert (v = 42);
  assert (Wrapper.get w2 = 43);
  Printf.printf "Wrapped: %d, Mapped: %d\n" v (Wrapper.get w2)

let () =
  approach1 ();
  approach2 ();
  approach3 ();
  Printf.printf "โœ“ All tests passed\n"

๐Ÿ“Š Detailed Comparison

Comparison: Deref Coercions

String to Str

OCaml โ€” one type, no conversion:

๐Ÿช Show OCaml equivalent
let greet name = Printf.printf "Hello, %s!\n" name
let () = greet "world"  (* just string *)

Rust โ€” auto deref:

fn greet(name: &str) { println!("Hello, {}!", name); }
let owned = String::from("world");
greet(&owned);  // &String auto-derefs to &str

Explicit vs Implicit

OCaml โ€” must convert explicitly:

๐Ÿช Show OCaml equivalent
let s = "hello" in
let bytes = Bytes.of_string s in  (* explicit *)

Rust โ€” deref handles it:

let s = String::from("hello");
let r: &str = &s;  // implicit via Deref

Smart Pointer Access

OCaml โ€” modules/abstract types need accessors:

๐Ÿช Show OCaml equivalent
let v = Wrapper.get w  (* explicit unwrap *)

Rust โ€” transparent via Deref:

let b = Box::new(42);
println!("{}", *b);     // explicit deref
println!("{}", b);      // auto-deref for Display