// Sealed traits: a public trait that external crates cannot implement.
//
// The pattern uses a private supertrait in a private module.
// External code can *use* the trait but cannot add new `impl`s for it.
// This gives library authors control over which types implement a trait.
// โโ The sealed-trait pattern โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
mod private {
/// A marker trait in a private module.
/// External code cannot name this trait, so it cannot impl Sealed for its types.
pub trait Sealed {}
}
/// A public trait with a private supertrait โ effectively sealed.
/// Any type T that implements `Shape` must also implement `private::Sealed`,
/// but external crates cannot write `impl private::Sealed for MyType`.
pub trait Shape: private::Sealed {
fn area(&self) -> f64;
fn perimeter(&self) -> f64;
fn name(&self) -> &'static str;
fn describe(&self) -> String {
format!(
"{}: area={:.4}, perimeter={:.4}",
self.name(),
self.area(),
self.perimeter()
)
}
}
// โโ Permitted implementors โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
// Only types inside this crate can implement Shape, because only they can
// also implement `private::Sealed`.
#[derive(Debug, Clone)]
pub struct Circle {
pub radius: f64,
}
#[derive(Debug, Clone)]
pub struct Rectangle {
pub width: f64,
pub height: f64,
}
#[derive(Debug, Clone)]
pub struct EquilateralTriangle {
pub side: f64,
}
// Seal each type
impl private::Sealed for Circle {}
impl private::Sealed for Rectangle {}
impl private::Sealed for EquilateralTriangle {}
impl Shape 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 Shape for Rectangle {
fn area(&self) -> f64 { self.width * self.height }
fn perimeter(&self) -> f64 { 2.0 * (self.width + self.height) }
fn name(&self) -> &'static str { "rectangle" }
}
impl Shape for EquilateralTriangle {
fn area(&self) -> f64 {
let s = self.side;
(3.0_f64.sqrt() / 4.0) * s * s
}
fn perimeter(&self) -> f64 { 3.0 * self.side }
fn name(&self) -> &'static str { "equilateral-triangle" }
}
// โโ Sealed token pattern โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
// An alternative use: pass a sealed token as a proof that the caller is
// in the "permitted" set โ like a capability token.
mod token_private {
pub trait TokenSeal {}
}
/// A token that only this crate can construct.
pub struct AdminToken(());
impl token_private::TokenSeal for AdminToken {}
pub trait AdminAction: token_private::TokenSeal {
/// Only callable with an AdminToken โ and only we can make AdminTokens.
fn execute(&self, _token: &AdminToken) -> String;
}
impl AdminAction for AdminToken {
fn execute(&self, _token: &AdminToken) -> String {
"admin action executed".to_string()
}
}
impl AdminToken {
/// The only way to create an AdminToken โ validates credentials.
pub fn authenticate(password: &str) -> Option<Self> {
if password == "hunter2" {
Some(AdminToken(()))
} else {
None
}
}
}
// โโ Generic function over sealed shapes โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
fn total_area(shapes: &[&dyn Shape]) -> f64 {
shapes.iter().map(|s| s.area()).sum()
}
fn largest<'a>(shapes: &[&'a dyn Shape]) -> Option<&'a dyn Shape> {
shapes.iter()
.max_by(|a, b| a.area().partial_cmp(&b.area()).unwrap())
.copied()
}
fn main() {
let c = Circle { radius: 5.0 };
let r = Rectangle { width: 3.0, height: 4.0 };
let t = EquilateralTriangle { side: 6.0 };
let shapes: Vec<&dyn Shape> = vec![&c, &r, &t];
println!("Shapes:");
for s in &shapes {
println!(" {}", s.describe());
}
println!("Total area: {:.4}", total_area(&shapes));
if let Some(big) = largest(&shapes) {
println!("Largest: {}", big.name());
}
// Sealed token
match AdminToken::authenticate("hunter2") {
Some(token) => println!("\n{}", token.execute(&token)),
None => println!("auth failed"),
}
match AdminToken::authenticate("wrong") {
None => println!("Bad password rejected"),
Some(_) => println!("unexpected"),
}
// Note: the following would NOT compile from an external crate:
// struct MyShape;
// impl private::Sealed for MyShape {} // error: `private` is private
// impl Shape for MyShape { ... }
println!("\nSealed trait pattern works โ external impls are impossible.");
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_circle_area() {
let c = Circle { radius: 1.0 };
assert!((c.area() - std::f64::consts::PI).abs() < 1e-10);
}
#[test]
fn test_rectangle() {
let r = Rectangle { width: 3.0, height: 4.0 };
assert_eq!(r.area(), 12.0);
assert_eq!(r.perimeter(), 14.0);
}
#[test]
fn test_triangle() {
let t = EquilateralTriangle { side: 2.0 };
let expected = (3.0_f64.sqrt() / 4.0) * 4.0;
assert!((t.area() - expected).abs() < 1e-10);
assert_eq!(t.perimeter(), 6.0);
}
#[test]
fn test_total_area() {
let r = Rectangle { width: 4.0, height: 5.0 };
let r2 = Rectangle { width: 2.0, height: 3.0 };
let shapes: Vec<&dyn Shape> = vec![&r, &r2];
assert!((total_area(&shapes) - 26.0).abs() < 1e-10);
}
#[test]
fn test_admin_token() {
assert!(AdminToken::authenticate("hunter2").is_some());
assert!(AdminToken::authenticate("bad").is_none());
}
#[test]
fn test_describe() {
let c = Circle { radius: 1.0 };
let desc = c.describe();
assert!(desc.starts_with("circle:"));
}
}
(* OCaml doesn't have sealed traits natively, but we can simulate them
with module types + private constructors. The pattern restricts who
can create instances of a type. *)
(* Shape hierarchy: only shapes defined here can implement Shape *)
module type SHAPE = sig
type t
val area : t -> float
val perimeter: t -> float
val name : string
end
module Circle : SHAPE = struct
type t = { radius: float }
let area c = Float.pi *. c.radius *. c.radius
let perimeter c = 2. *. Float.pi *. c.radius
let name = "circle"
end
module Rect : SHAPE = struct
type t = { w: float; h: float }
let area r = r.w *. r.h
let perimeter r = 2. *. (r.w +. r.h)
let name = "rectangle"
end
(* Existential wrapper for dispatch *)
type shape = Shape : (module SHAPE with type t = 'a) * 'a -> shape
let circle r = Shape ((module Circle), { Circle.radius = r })
let rect w h = Shape ((module Rect), { Rect.w = w; Rect.h = h })
let area (Shape ((module S), s)) = S.area s
let perimeter (Shape ((module S), s)) = S.perimeter s
let name (Shape ((module S), _)) = S.name
let () =
let shapes = [circle 5.; rect 3. 4.; circle 1.] in
List.iter (fun s ->
Printf.printf "%s: area=%.2f perimeter=%.2f\n"
(name s) (area s) (perimeter s)
) shapes