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

077: Generic Bounds

Difficulty: Intermediate Category: Traits & Generics Concept: Constraining generic types with trait bounds `<T: Display + Clone>` Key Insight: OCaml uses parametric polymorphism (no bounds needed); Rust requires explicit trait bounds to use methods on generic types.
// 077: Generic Bounds
// Constraining generic types with trait bounds

use std::fmt::Display;

// Approach 1: Single bound
fn print_item<T: Display>(item: &T) -> String {
    format!("{}", item)
}

fn print_list<T: Display>(items: &[T]) -> String {
    let strs: Vec<String> = items.iter().map(|x| format!("{}", x)).collect();
    format!("[{}]", strs.join(", "))
}

// Approach 2: Multiple bounds
fn print_and_clone<T: Display + Clone>(item: &T) -> (String, T) {
    (format!("{}", item), item.clone())
}

fn find_max<T: PartialOrd + Clone>(items: &[T]) -> Option<T> {
    items.iter().cloned().reduce(|a, b| if a >= b { a } else { b })
}

// Approach 3: Bounds for arithmetic
fn sum_items<T: std::iter::Sum + Copy>(items: &[T]) -> T {
    items.iter().copied().sum()
}

fn contains<T: PartialEq>(items: &[T], target: &T) -> bool {
    items.iter().any(|x| x == target)
}

// Generic pair with bounds
fn larger<T: PartialOrd>(a: T, b: T) -> T {
    if a >= b { a } else { b }
}

fn main() {
    println!("{}", print_item(&42));
    println!("{}", print_list(&[1, 2, 3]));
    println!("max: {:?}", find_max(&[3, 1, 4, 1, 5]));
    println!("larger: {}", larger(10, 20));
}

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

    #[test]
    fn test_print_item() {
        assert_eq!(print_item(&42), "42");
        assert_eq!(print_item(&"hello"), "hello");
    }

    #[test]
    fn test_print_list() {
        assert_eq!(print_list(&[1, 2, 3]), "[1, 2, 3]");
    }

    #[test]
    fn test_print_and_clone() {
        let (s, v) = print_and_clone(&42);
        assert_eq!(s, "42");
        assert_eq!(v, 42);
    }

    #[test]
    fn test_find_max() {
        assert_eq!(find_max(&[3, 1, 4, 1, 5]), Some(5));
        assert_eq!(find_max::<i32>(&[]), None);
    }

    #[test]
    fn test_contains() {
        assert!(contains(&[1, 2, 3], &2));
        assert!(!contains(&[1, 2, 3], &4));
    }

    #[test]
    fn test_larger() {
        assert_eq!(larger(10, 20), 20);
        assert_eq!(larger("z", "a"), "z");
    }
}
(* 077: Generic Bounds โ€” OCaml parametric polymorphism *)
(* OCaml doesn't need bounds โ€” polymorphism is structural *)

(* Approach 1: Generic identity and pair โ€” no bounds needed *)
let identity x = x
let make_pair a b = (a, b)
let swap (a, b) = (b, a)

(* Approach 2: Functors as "bounded" generics *)
module type PRINTABLE = sig
  type t
  val to_string : t -> string
end

module type COMPARABLE = sig
  type t
  val compare : t -> t -> int
end

module Printer (P : PRINTABLE) = struct
  let print_list lst =
    let strs = List.map P.to_string lst in
    "[" ^ String.concat "; " strs ^ "]"
end

module IntPrint = Printer(struct
  type t = int
  let to_string = string_of_int
end)

(* Approach 3: Using polymorphic comparison *)
let find_max lst =
  match lst with
  | [] -> None
  | x :: xs -> Some (List.fold_left max x xs)

let contains lst x = List.exists (fun e -> e = x) lst

(* Tests *)
let () =
  assert (identity 42 = 42);
  assert (identity "hello" = "hello");
  assert (make_pair 1 "a" = (1, "a"));
  assert (swap (1, 2) = (2, 1));
  assert (IntPrint.print_list [1; 2; 3] = "[1; 2; 3]");
  assert (find_max [3; 1; 4; 1; 5] = Some 5);
  assert (find_max [] = None);
  assert (contains [1; 2; 3] 2);
  assert (not (contains [1; 2; 3] 4));
  Printf.printf "โœ“ All tests passed\n"

๐Ÿ“Š Detailed Comparison

Core Insight

Rust generics require explicit bounds: `<T: Display>` means "T must implement Display". OCaml generics are unconstrained โ€” any type works if the operations typecheck structurally.

OCaml Approach

  • `'a` is universally polymorphic โ€” no constraints
  • Functors for module-level constraints
  • No trait bounds โ€” structural typing suffices

Rust Approach

  • `<T: Trait>` syntax for inline bounds
  • Multiple bounds: `<T: Display + Clone>`
  • Bounds required to call methods on generic T

Comparison Table

FeatureOCamlRust
Syntax`'a` (unconstrained)`<T: Bound>`
MultipleN/A`T: A + B`
Required?NoYes, to use methods
CheckedAt use siteAt declaration