// 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"