// 076: Trait Objects โ dynamic dispatch with dyn Trait
use std::f64::consts::PI;
// Approach 1: Define trait and implementations
trait Shape {
fn area(&self) -> f64;
fn name(&self) -> &str;
}
struct Circle { radius: f64 }
struct Rectangle { width: f64, height: f64 }
impl Shape for Circle {
fn area(&self) -> f64 { PI * self.radius * self.radius }
fn name(&self) -> &str { "circle" }
}
impl Shape for Rectangle {
fn area(&self) -> f64 { self.width * self.height }
fn name(&self) -> &str { "rectangle" }
}
// Approach 2: Using dyn Trait for polymorphism
fn describe(shape: &dyn Shape) -> String {
format!("{} with area {:.2}", shape.name(), shape.area())
}
fn total_area(shapes: &[Box<dyn Shape>]) -> f64 {
shapes.iter().map(|s| s.area()).sum()
}
// Approach 3: Returning trait objects
fn make_shape(kind: &str) -> Box<dyn Shape> {
match kind {
"circle" => Box::new(Circle { radius: 5.0 }),
"rectangle" => Box::new(Rectangle { width: 3.0, height: 4.0 }),
_ => Box::new(Circle { radius: 1.0 }),
}
}
fn main() {
let shapes: Vec<Box<dyn Shape>> = vec![
Box::new(Circle { radius: 5.0 }),
Box::new(Rectangle { width: 3.0, height: 4.0 }),
];
for s in &shapes {
println!("{}", describe(s.as_ref()));
}
println!("Total area: {:.2}", total_area(&shapes));
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_circle() {
let c = Circle { radius: 5.0 };
assert!((c.area() - 78.54).abs() < 0.01);
assert_eq!(c.name(), "circle");
}
#[test]
fn test_rectangle() {
let r = Rectangle { width: 3.0, height: 4.0 };
assert_eq!(r.area(), 12.0);
}
#[test]
fn test_dyn_dispatch() {
let shapes: Vec<Box<dyn Shape>> = vec![
Box::new(Circle { radius: 5.0 }),
Box::new(Rectangle { width: 3.0, height: 4.0 }),
];
assert!((total_area(&shapes) - 90.54).abs() < 0.01);
}
#[test]
fn test_make_shape() {
let s = make_shape("circle");
assert_eq!(s.name(), "circle");
}
}
(* 076: Trait Objects โ OCaml objects for dynamic dispatch *)
(* Approach 1: OCaml object types *)
class virtual shape = object
method virtual area : float
method virtual name : string
end
class circle r = object
inherit shape
method area = Float.pi *. r *. r
method name = "circle"
end
class rectangle w h = object
inherit shape
method area = w *. h
method name = "rectangle"
end
(* Approach 2: Using objects polymorphically *)
let describe (s : shape) =
Printf.sprintf "%s with area %.2f" s#name s#area
let total_area (shapes : shape list) =
List.fold_left (fun acc s -> acc +. s#area) 0.0 shapes
(* Approach 3: First-class modules as alternative *)
module type SHAPE = sig
val area : unit -> float
val name : unit -> string
end
let make_circle r : (module SHAPE) =
(module struct
let area () = Float.pi *. r *. r
let name () = "circle"
end)
let describe_mod (module S : SHAPE) =
Printf.sprintf "%s with area %.2f" (S.name ()) (S.area ())
(* Tests *)
let () =
let c = new circle 5.0 in
let r = new rectangle 3.0 4.0 in
assert (abs_float (c#area -. 78.54) < 0.01);
assert (r#area = 12.0);
assert (c#name = "circle");
let shapes = [c :> shape; r :> shape] in
assert (abs_float (total_area shapes -. 90.54) < 0.01);
let mc = make_circle 5.0 in
let desc = describe_mod mc in
assert (String.sub desc 0 6 = "circle");
Printf.printf "โ All tests passed\n"