use closed_trait::sealed;
pub struct Square;
pub mod shapes {
pub struct Circle;
}
#[sealed(Square, crate::shapes::Circle)]
pub trait Shape {
fn area(&self) -> f64;
}
impl Shape for Square {
fn area(&self) -> f64 {
1.0
}
}
impl Shape for shapes::Circle {
fn area(&self) -> f64 {
std::f64::consts::PI
}
}
#[test]
fn listed_types_implement_the_sealed_trait() {
assert_eq!(Square.area(), 1.0);
assert_eq!(shapes::Circle.area(), std::f64::consts::PI);
}
#[test]
fn the_trait_is_usable_without_naming_the_seal() {
fn total(shapes: &[&dyn Shape]) -> f64 {
shapes.iter().map(|shape| shape.area()).sum()
}
assert_eq!(total(&[&Square, &Square]), 2.0);
}
#[sealed(Named)]
trait Describe: std::fmt::Debug {
fn describe(&self) -> String {
format!("{self:?}")
}
}
#[derive(Debug)]
struct Named;
impl Describe for Named {}
#[test]
fn existing_supertraits_are_kept() {
assert_eq!(Named.describe(), "Named");
}
#[sealed(Borrowed<'_>)]
trait Borrow<'a> {
fn text(&self) -> &'a str;
}
struct Borrowed<'a>(&'a str);
impl<'a> Borrow<'a> for Borrowed<'a> {
fn text(&self) -> &'a str {
self.0
}
}
#[test]
fn traits_with_lifetimes_seal_too() {
let source = String::from("held");
assert_eq!(Borrowed(&source).text(), "held");
}
mod binders {
use closed_trait::sealed;
pub struct Slice<'a>(pub &'a str);
pub struct Held(pub String);
#[sealed(for<'a> Slice<'a>, Held)]
pub trait Text {
fn text(&self) -> &str;
}
impl<'a> Text for Slice<'a> {
fn text(&self) -> &str {
self.0
}
}
impl Text for Held {
fn text(&self) -> &str {
&self.0
}
}
#[sealed(for<'a> Slice<'a>: Quoted<'a>)]
pub trait Quoted<'a> {
fn quoted(&self) -> String;
}
impl<'a> Quoted<'a> for Slice<'a> {
fn quoted(&self) -> String {
format!("{:?}", self.0)
}
}
}
#[test]
fn a_lifetime_comes_from_the_trait_or_from_a_binder() {
use binders::{Held, Quoted, Slice, Text};
let owned = String::from("hi");
assert_eq!(Slice(&owned).text(), "hi");
assert_eq!(Held(owned.clone()).text(), "hi");
assert_eq!(Slice(&owned).quoted(), "\"hi\"");
}
mod lifetime_only {
use closed_trait::sealed;
#[sealed(Pinned, Any)]
pub trait Held<'a> {
fn held(&self) -> i32;
}
pub struct Pinned;
pub struct Any;
impl Held<'static> for Pinned {
fn held(&self) -> i32 {
1
}
}
impl<'a> Held<'a> for Any {
fn held(&self) -> i32 {
2
}
}
}
#[test]
fn a_lifetime_only_trait_needs_no_annotation() {
use lifetime_only::{Any, Held, Pinned};
assert_eq!(Pinned.held(), 1);
assert_eq!(Any.held(), 2);
}
mod annotated_lifetime {
use closed_trait::sealed;
#[sealed(Pinned: Kept<'static>)]
pub trait Kept<'a> {
fn kept(&self) -> i32;
}
pub struct Pinned;
impl Kept<'static> for Pinned {
fn kept(&self) -> i32 {
3
}
}
}
#[test]
fn an_annotated_lifetime_is_harmless() {
use annotated_lifetime::{Kept, Pinned};
assert_eq!(Pinned.kept(), 3);
}
mod several_instantiations {
use closed_trait::sealed;
#[sealed(Plain: Store<i32>, Plain: Store<f64>, Fixed: Store<u8>)]
pub trait Store<T> {
fn get(&self) -> T;
}
pub struct Plain;
pub struct Fixed;
impl Store<i32> for Plain {
fn get(&self) -> i32 {
1
}
}
impl Store<f64> for Plain {
fn get(&self) -> f64 {
2.0
}
}
impl Store<u8> for Fixed {
fn get(&self) -> u8 {
3
}
}
}
#[test]
fn one_type_may_be_listed_at_several_instantiations() {
use several_instantiations::{Fixed, Plain, Store};
assert_eq!(Store::<i32>::get(&Plain), 1);
assert_eq!(Store::<f64>::get(&Plain), 2.0);
assert_eq!(Fixed.get(), 3u8);
}
mod shared_type_distinct_enums {
use closed_trait::{enumerate, sealed};
#[enumerate]
#[sealed(Plain: Keep<i32>, Plain as PlainF64: Keep<f64>, for<T> Boxed<T>: Keep<T>)]
pub trait Keep<T> {}
pub struct Plain;
pub struct Boxed<T>(pub T);
impl Keep<i32> for Plain {}
impl Keep<f64> for Plain {}
impl<T> Keep<T> for Boxed<T> {}
}
#[test]
fn a_shared_type_may_pin_different_arguments() {
use closed_trait::Enumerable;
use shared_type_distinct_enums::{AnyKeep, Boxed, Plain};
let as_i32: AnyKeep<i32> = Plain.into_enum();
let as_f64: AnyKeep<f64> = Plain.into_enum();
assert!(matches!(as_i32, AnyKeep::Plain(_)));
assert!(matches!(as_f64, AnyKeep::PlainF64(_)));
let boxed: AnyKeep<u8> = Boxed(1u8).into_enum();
assert!(matches!(boxed, AnyKeep::Boxed(_)));
}
mod every_part {
use closed_trait::sealed;
pub struct Shown<'x, U>(pub &'x U);
#[sealed(for<'x, U: Clone> Shown<'x, U> as Displayed: Store<i32>)]
pub trait Store<T> {
fn count(&self) -> usize;
}
impl<'x, U: Clone> Store<i32> for Shown<'x, U> {
fn count(&self) -> usize {
core::mem::size_of_val(self.0)
}
}
}
#[test]
fn an_entry_may_use_every_part_at_once() {
use every_part::{Shown, Store};
let value = 7i32;
assert_eq!(Shown(&value).count(), 4);
}
mod precise {
use closed_trait::sealed;
#[sealed(Plain: Store<i32>, for<T> Boxed<T>: Store<T>)]
pub trait Store<T> {
fn size(&self) -> usize;
}
pub struct Plain;
pub struct Boxed<T>(pub T);
impl Store<i32> for Plain {
fn size(&self) -> usize {
1
}
}
impl<T> Store<T> for Boxed<T> {
fn size(&self) -> usize {
core::mem::size_of_val(&self.0)
}
}
}
#[test]
fn an_entry_permits_only_the_instantiation_it_names() {
use precise::{Boxed, Plain, Store};
assert_eq!(Store::<i32>::size(&Plain), 1);
assert_eq!(Boxed(2u8).size(), 1);
assert_eq!(Boxed(3.5f64).size(), 8);
}
mod every_instantiation {
use closed_trait::sealed;
#[sealed(for<X> Plain: Store<X>, Pinned: Store<i32>)]
pub trait Store<X> {
fn size(&self) -> usize;
}
pub struct Plain;
pub struct Pinned;
impl<X> Store<X> for Plain {
fn size(&self) -> usize {
1
}
}
impl Store<i32> for Pinned {
fn size(&self) -> usize {
2
}
}
}
#[test]
fn an_entry_may_name_the_traits_parameters_in_its_instantiation() {
use every_instantiation::{Pinned, Plain, Store};
assert_eq!(Store::<i32>::size(&Plain), 1);
assert_eq!(Store::<f64>::size(&Plain), 1);
assert_eq!(Store::<i32>::size(&Pinned), 2);
}
mod nested_instantiation {
use closed_trait::sealed;
#[sealed(for<T> Keyed<T>: Store<Vec<T>>)]
pub trait Store<X> {
fn size(&self) -> usize;
}
pub struct Keyed<T>(pub T);
impl<T> Store<Vec<T>> for Keyed<T> {
fn size(&self) -> usize {
1
}
}
}
#[test]
fn an_instantiation_may_nest_a_binders_parameter() {
use nested_instantiation::{Keyed, Store};
assert_eq!(Store::<Vec<i32>>::size(&Keyed(1i32)), 1);
}
mod generic_type {
use closed_trait::sealed;
use core::fmt::Debug;
pub struct Boxed<T>(pub T);
pub struct Shown<T>(pub T);
pub struct NotDebug;
#[sealed(for<T> Boxed<T>, for<T: Debug> Shown<T>)]
pub trait Shape {
fn describe(&self) -> &'static str;
}
impl<T> Shape for Boxed<T> {
fn describe(&self) -> &'static str {
"boxed"
}
}
impl<T: Debug> Shape for Shown<T> {
fn describe(&self) -> &'static str {
"shown"
}
}
}
#[test]
fn a_binder_seals_a_generic_type_under_a_plain_trait() {
use generic_type::{Boxed, NotDebug, Shape, Shown};
assert_eq!(Boxed(NotDebug).describe(), "boxed");
assert_eq!(Shown(1).describe(), "shown");
}
mod nothing_permitted {
use closed_trait::sealed;
#[sealed]
pub trait Bare {}
#[sealed()]
pub trait Parenthesised {}
#[allow(dead_code)]
fn bare<T: Bare>(_: T) {}
#[allow(dead_code)]
fn parenthesised<T: Parenthesised>(_: T) {}
}