// Type erasure: hide a concrete type behind a trait object.
// The concrete type is gone ("erased") at runtime; only the trait interface remains.
//
// OCaml uses first-class modules (existential types) for this.
// Rust uses `Box<dyn Trait>` (or `Arc<dyn Trait>` for shared ownership).
use std::fmt;
// โโ Showable: any value that can be displayed โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
pub trait Showable {
fn show(&self) -> String;
fn type_name(&self) -> &'static str;
}
impl Showable for i32 {
fn show(&self) -> String { self.to_string() }
fn type_name(&self) -> &'static str { "i32" }
}
impl Showable for f64 {
fn show(&self) -> String { format!("{}", self) }
fn type_name(&self) -> &'static str { "f64" }
}
impl Showable for bool {
fn show(&self) -> String { self.to_string() }
fn type_name(&self) -> &'static str { "bool" }
}
impl Showable for String {
fn show(&self) -> String { format!("\"{}\"", self) }
fn type_name(&self) -> &'static str { "String" }
}
impl Showable for Vec<i32> {
fn show(&self) -> String {
let inner: Vec<String> = self.iter().map(|x| x.to_string()).collect();
format!("[{}]", inner.join(", "))
}
fn type_name(&self) -> &'static str { "Vec<i32>" }
}
/// A type-erased showable value.
pub struct AnyShow(Box<dyn Showable>);
impl AnyShow {
pub fn new<T: Showable + 'static>(v: T) -> Self {
AnyShow(Box::new(v))
}
pub fn show(&self) -> String { self.0.show() }
pub fn type_name(&self) -> &'static str { self.0.type_name() }
}
impl fmt::Display for AnyShow {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} ({})", self.show(), self.type_name())
}
}
// โโ Drawable: shapes with area and perimeter โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
pub trait Drawable: fmt::Debug {
fn area(&self) -> f64;
fn perimeter(&self) -> f64;
fn name(&self) -> &'static str;
fn describe(&self) -> String {
format!("{}: area={:.2} perimeter={:.2}", self.name(), self.area(), self.perimeter())
}
}
#[derive(Debug)]
pub struct Circle { pub radius: f64 }
#[derive(Debug)]
pub struct Rect { pub w: f64, pub h: f64 }
#[derive(Debug)]
pub struct Triangle { pub base: f64, pub height: f64, pub a: f64, pub b: f64, pub c: f64 }
impl Drawable for Circle {
fn area(&self) -> f64 { std::f64::consts::PI * self.radius * self.radius }
fn perimeter(&self) -> f64 { 2.0 * std::f64::consts::PI * self.radius }
fn name(&self) -> &'static str { "circle" }
}
impl Drawable for Rect {
fn area(&self) -> f64 { self.w * self.h }
fn perimeter(&self) -> f64 { 2.0 * (self.w + self.h) }
fn name(&self) -> &'static str { "rectangle" }
}
impl Drawable for Triangle {
fn area(&self) -> f64 { 0.5 * self.base * self.height }
fn perimeter(&self) -> f64 { self.a + self.b + self.c }
fn name(&self) -> &'static str { "triangle" }
}
/// A heterogeneous scene: different shape types, same interface.
pub struct Scene {
shapes: Vec<Box<dyn Drawable>>,
}
impl Scene {
pub fn new() -> Self { Scene { shapes: Vec::new() } }
pub fn add(mut self, s: impl Drawable + 'static) -> Self {
self.shapes.push(Box::new(s));
self
}
pub fn total_area(&self) -> f64 {
self.shapes.iter().map(|s| s.area()).sum()
}
pub fn describe_all(&self) {
for s in &self.shapes {
println!(" {}", s.describe());
}
}
pub fn largest_area(&self) -> Option<&dyn Drawable> {
self.shapes.iter()
.max_by(|a, b| a.area().partial_cmp(&b.area()).unwrap())
.map(|b| b.as_ref())
}
}
// โโ Function-erased callbacks โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
pub struct Handler {
callback: Box<dyn Fn(&str) -> String>,
}
impl Handler {
pub fn new<F: Fn(&str) -> String + 'static>(f: F) -> Self {
Handler { callback: Box::new(f) }
}
pub fn handle(&self, input: &str) -> String {
(self.callback)(input)
}
}
fn main() {
// โโ Showable type erasure โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
let items: Vec<AnyShow> = vec![
AnyShow::new(42_i32),
AnyShow::new(3.14_f64),
AnyShow::new(true),
AnyShow::new("hello".to_string()),
AnyShow::new(vec![1, 2, 3]),
];
println!("Erased values:");
for item in &items {
println!(" {}", item);
}
// โโ Shape scene โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
let scene = Scene::new()
.add(Circle { radius: 5.0 })
.add(Rect { w: 3.0, h: 4.0 })
.add(Triangle { base: 6.0, height: 4.0, a: 5.0, b: 5.0, c: 6.0 });
println!("\nShapes:");
scene.describe_all();
println!("Total area: {:.2}", scene.total_area());
if let Some(largest) = scene.largest_area() {
println!("Largest: {}", largest.name());
}
// โโ Erased callbacks โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
let handlers: Vec<Handler> = vec![
Handler::new(|s| s.to_uppercase()),
Handler::new(|s| s.chars().rev().collect()),
Handler::new(|s| format!("len={}", s.len())),
];
println!("\nHandlers on 'hello':");
for h in &handlers {
println!(" {}", h.handle("hello"));
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_any_show_i32() {
let a = AnyShow::new(42_i32);
assert_eq!(a.show(), "42");
assert_eq!(a.type_name(), "i32");
}
#[test]
fn test_any_show_string() {
let a = AnyShow::new("hello".to_string());
assert_eq!(a.show(), "\"hello\"");
}
#[test]
fn test_scene_area() {
let scene = Scene::new()
.add(Rect { w: 4.0, h: 5.0 })
.add(Rect { w: 2.0, h: 3.0 });
assert!((scene.total_area() - 26.0).abs() < 0.001);
}
#[test]
fn test_erased_shapes_hetero() {
let scene = Scene::new()
.add(Circle { radius: 1.0 })
.add(Rect { w: 10.0, h: 10.0 });
assert_eq!(scene.shapes.len(), 2);
// Rect has area 100, Circle has area ~3.14 โ largest is Rect
assert_eq!(scene.largest_area().unwrap().name(), "rectangle");
}
#[test]
fn test_handler_erasure() {
let h = Handler::new(|s: &str| s.len().to_string());
assert_eq!(h.handle("hello"), "5");
}
#[test]
fn test_vec_showable() {
let a = AnyShow::new(vec![1_i32, 2, 3]);
assert_eq!(a.show(), "[1, 2, 3]");
}
}
(* Type erasure via existential types (first-class modules in OCaml).
Pack a value with its operations; the concrete type is hidden. *)
(* A "showable" value: we only know it can be converted to string *)
module type SHOWABLE = sig
type t
val value : t
val show : t -> string
end
type showable = (module SHOWABLE)
let pack_int n : showable =
(module struct
type t = int
let value = n
let show = string_of_int
end)
let pack_float f : showable =
(module struct
type t = float
let value = f
let show = string_of_float
end)
let pack_bool b : showable =
(module struct
type t = bool
let value = b
let show = string_of_bool
end)
let show_any (s : showable) =
let module S = (val s) in
S.show S.value
let () =
let items = [
pack_int 42;
pack_float 3.14;
pack_bool true;
pack_int (-7);
] in
Printf.printf "Erased values:\n";
List.iter (fun s -> Printf.printf " %s\n" (show_any s)) items