//! Bijective mapping between types with and without lifetimes. //! //! Types that implement [`Static`] contain no lifetimes (other than `'static`). //! Types that implement [`WithLt`] contain zero, one, or two lifetimes. //! The [`Raised`] and [`Lowered`] type aliases can be used to switch between the //! type having the lifetimes and the type that doesn't. //! //! In type theory terms, [`Static`] types represent type constructors needing //! the two lifetimes `'lt` and `'ctx` (where `'ctx` outlives `'lt`). //! [`WithLt`] represents the types that have a type constructor of the [`Static`] //! form. /// Lowers a higher ranked type to one with specific lifetimes. /// /// This acts to *add* the `'lt` and `'ctx` lifetime to the type. /// /// Note the `Bound` generic exists for implied lifetime bounds to make /// `'ctx: 'lt` work with Rust's `for<'a>` syntax. pub trait Lower<'lt, 'ctx: 'lt, Bound>: 'static { /// The lowered type with lifetimes. type Lowered: ?Sized + Raise<'lt, 'ctx, &'lt &'ctx (), Raised = Self>; } /// Raises a type to one without lifetimes. /// /// This acts to *subtract* the `'lt` and `'ctx` lifetime from the type. /// /// Note the `Bound` generic exists for implied lifetime bounds to make /// `'ctx: 'lt` work with Rust's `for<'a>` syntax. pub trait Raise<'lt, 'ctx: 'lt, Bound>: 'lt { /// The raised type without lifetimes. type Raised: ?Sized + Lower<'lt, 'ctx, &'lt &'ctx (), Lowered = Self>; } /// A type without lifetimes (other than `'static`). /// /// These types can be lowered to types with lifetimes using [`Lowered`]. pub trait Static: 'static + for<'lt, 'ctx> Lower<'lt, 'ctx, &'lt &'ctx ()> {} impl Static for T where T: 'static + for<'lt, 'ctx> Lower<'lt, 'ctx, &'lt &'ctx ()> {} /// A type with zero, one, or two lifetimes. /// /// These types can be raised to types without lifetimes using [`Raised`]. pub trait WithLt<'lt, 'ctx: 'lt>: Raise<'lt, 'ctx, &'lt &'ctx ()> {} impl<'lt, 'ctx: 'lt, T: ?Sized> WithLt<'lt, 'ctx> for T where T: Raise<'lt, 'ctx, &'lt &'ctx ()> {} /// Add lifetimes to a type without them. pub type Lowered<'lt, 'ctx, T> = >::Lowered; /// Remove lifetimes from a type with them. pub type Raised<'lt, 'ctx, T> = >::Raised; fn _is_bijective_raise<'lt, 'ctx: 'lt, T>(x: Lowered<'lt, 'ctx, Raised<'lt, 'ctx, T>>) where T: WithLt<'lt, 'ctx>, { // This assignment shows T == Lowered> let _y: T = x; } fn _is_bijective_lower<'lt, 'ctx: 'lt, U>(x: Raised<'lt, 'ctx, Lowered<'lt, 'ctx, U>>) where U: Static, { // This assignment shows U == Raised> let _y: U = x; } // A = set of Static types // B = set of lifetime types // // We only need one of the functions above to show its a bijection. // If we go a -> b -> a (a: A, b: B) for any a in A // then we know that there must only exist cycles of length 2. // This doesn't not however prove by itself that the b we encountered // is not reused. For that we must consider that we do b -> a using // Raised which has no information about the a we came from. // As such, no other a could exist that uses b in it's cycle. // This proves the two sets are of equal size and that the Lowered // and Raised form a bijective mapping both ways. // // Impossible: // // struct A; // struct B; // struct C; // // impl<'a, 'b> Lower<'a, 'b, &'a &'b ()> for A { // type Lowered = B; // } // // impl<'a, 'b> Raise<'a, 'b, &'a &'b ()> for B { // type Raised = A; // } // // impl<'a, 'b> Lower<'a, 'b, &'a &'b ()> for C { // type Lowered = B; // }