1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344
//! This crate provides the [`transient::Any`] trait which re-implements the
//! dynamic typing mechanism provided by [`std::any::Any`] to add support for
//! types with non-`'static` lifetimes.
//!
//! # Introduction
//! The standard library's [`Any`] trait is used to emulate dynamic typing within
//! Rust, and is extremely useful in cases where implementing a statically typed
//! solution would be inconvenient, if not impossible. Examples include storing
//! heterogeneous values in a `Vec`, or eliminating generic parameters from a
//! type so that it can be used in object-safe trait methods.
//!
//! However, a significant limitation of the `std::any::Any` trait is its `'static`
//! lifetime bound, which prevents it from being used for types containing any
//! non-`'static` references. This restriction eliminates many potential use-cases,
//! and in others it can force users to sacrifice performance by cloning data that
//! could otherwise be borrowed.
//!
//! The crate provides a re-implemented [`Any`] trait that circumvents this limitation
//! to allow type-erasure to be applied to *transient* (i.e. non-`'static`) types.
//! This is achieved by modeling a Rust type as decomposable into separate components
//! for its _raw static data_ and its _lifetime parameters_, as embodied by the
//! `Static` and `Transience` associated types of the provided [`Transient`] trait.
//! In this implementation, the `Static` component is used to obtain the unique
//! [`TypeId`] of the type (which the compiler only hands out for `T: 'static`),
//! and the `Transience` is used as a generic parameter on the re-implemented
//! [`Any`] trait to bound the allowable transitions and uphold Rust's strict
//! safety guarantees.
//!
//! # Features
//! - Near drop-in replacement for `std::any::Any` when dealing with `'static` types
//! - Familiar extension beyond `std::any::Any` when dealing with non-`'static` types
//! - Zero run-time cost above that of a standard `dyn Any` cast, with all added
//! functionality implemented using the type system
//! - Safely accounts for the nuances of _subtyping and variance_
//! - Supports types with any number of generic lifetime parameters with arbitrary
//! variance combinations
//! - Supports types with any number of generic type parameters
//! - Provides the [`macro@Transient`] `derive` macro to implement the `Transient`
//! trait for most types
//!
//! # Limitations
//! - Requires a single `unsafe` trait to be implemented for types wishing to
//! utilize the crate's functionality; however, this trait is usually trivial
//! to safely implement, and a `derive` macro is provided for common cases
//! - Only emulates the non-`Send`/`Sync` variant of the stdlib's `Any` trait
//! for now, but support for `Any + Send` and `Any + Send + Sync` can be added
//! if requested
//! - Only `Sized` types are supported. Removing this restriction would be
//! trivial, but makes it awkward to name generic types that require their
//! parameters to be `T: Sized` since `T::Static: Sized` must be explicitly
//! stated even when `T: Sized` can be implied
//!
//! # Examples
//!
//! The first step in using this crate is to implement the [`Transient`] trait
//! for a type. Implementations of this trait are provided for many stdlib
//! types, it can be derived for most custom types, and it is easy to implement
//! by hand when more flexibility is needed. Implementations for common types
//! provided by some 3rd party libraries are also available behind eponymous
//! feature flags (currently only `ndarray`, `pyo3`, and `numpy`, but feel free
//! to submit an issue/PR requesting others).
//!
//! In the trivial case of a `'static` type with no lifetime parameters, the
//! `transient` crate's [`Any`] trait can be used just like that of the standard
//! library once the `Transient` trait has been implemented or derived:
//! ```
//! # fn main() {
//! use transient::*;
//!
//! #[derive(Transient, Debug, PartialEq)]
//! struct Usize(usize);
//!
//! let orig = Usize(5);
//!
//! let erased: &dyn Any = &orig;
//! assert_eq!(TypeId::of::<Usize>(), erased.type_id());
//!
//! let restored: &Usize = erased.downcast_ref::<Usize>().unwrap();
//! assert_eq!(restored, &orig);
//! # }
//! ```
//! The trick is that the `Any` trait as used above is actually generic over a
//! type known as the `Transience`, which defaults to `()`; so the relevant line
//! in the above snippet actually desugars to `erased: &'_ dyn Any<()> = &orig`.
//! This form of the `Any` trait only supports `'static` types, just like the
//! stdlib implementation.
//!
//! Where it gets interesting is when a type is *not* `'static`, for which the
//! `Any` trait can be parameterized by a [`Transience`] type. In the case of
//! a type with a single lifetime parameter, this can simply be one of three types
//! provided by this crate, [`Inv`], [`Co`], and [`Contra`], which represent the
//! three flavors of [variance] a type can have with respect to a lifetime parameter.
//! While choosing the correct variance would typically be a safety-critical
//! decision, the valid choices for the variance of a type are bounded by its
//! implementation of the `Transient` trait, and the compiler will prevent you
//! from using a transience that would not be sound.
//!
//! We will return to the topic of `Transience` in a bit, but for now lets choose
//! `Inv` (*invariant*) which is the most conservative form of variance that all
//! (single-lifetime) types can use. To do this, simply replace `dyn Any` with
//! `dyn Any<Inv>` when coercing a `Box` or reference to the trait object:
//! ```
//! # fn main() {
//! use transient::*;
//!
//! #[derive(Transient, Debug, PartialEq)]
//! struct UsizeRef<'a>(&'a usize);
//!
//! let five = 5;
//! let orig = UsizeRef(&five);
//!
//! let erased: &dyn Any<Inv> = &orig;
//! assert!(erased.is::<UsizeRef>());
//! assert_eq!(TypeId::of::<UsizeRef>(), erased.type_id());
//!
//! let restored: &UsizeRef = erased.downcast_ref().unwrap();
//! assert_eq!(restored, &orig);
//! # }
//! ```
//!
//! And that's all it takes! Things get a slightly spicier in more complicated
//! scenarios, but this crate aims to make the process as painless and intuitive
//! as possible while safely providing a high degree of flexibility for all the
//! niche cases you can imagine.
//!
//!
//! # Overview
//!
//! ## The `Any` trait
//! The most important item provided by this crate is the [`Any`] trait, which is
//! modeled after the standard library's [`std::any::Any`] trait. Much like the
//! stdlib version, this trait typically appears as the opaque `dyn Any` trait
//! object that can be _downcast_ back into an original concrete type. The key
//! difference is that, while the `std::any::Any` trait is implemented for all
//! `T: 'static`, the [`transient::Any`] trait is instead implemented for all
//! `T: Transient` (as discussed in the next section). The `transient::Any`
//! trait is also different in that it has a generic type parameter know as
//! he `Transience` (discussed in another upcoming section) which is used to
//! enable the support for non-`'static` types that forms the motivation for
//! this crate.
//!
//! ## The `Transient` Trait
//! The [`Transient`] trait is an extremely simple, but `unsafe` trait consisting
//! only of two associated types:
//! ```skip
//! pub unsafe trait Transient {
//! type Static: 'static;
//! type Transience: Transience;
//! /* provided methods hidden */
//! }
//! ```
//! The first associated type `Static` is referred to as the *static type* of the
//! implementing type, and is simply the same type but with its lifetime parameters
//! replaced by `'static` (e.g., a struct `S<'a, 'b>` would define `Static` as
//! `S<'static, 'static>`). The static type is used to obtain a [`TypeId`] that
//! uniquely identifies the (`'static` version of the) erased type so that it can
//! be safely downcast from an opaque trait object to the concrete type. However,
//! the compiler only assigns `TypeId`s for `'static` types, so any information
//! about the true lifetime parameters of the `Transient` type is lost. Another
//! mechanism is therefore needed to restore this lifetime information so that
//! the borrow checker can continue to maintain Rust's safety guarantees.
//!
//! The second associated type `Transience` provides this mechanism by capturing
//! the lifetime (and *[variance]*) information that the static type is missing.
//! To accomplish this, the `transient` crate provides the `Co`, `Contra` and `Inv`
//! structs that exhibit the 3 forms of variance for a single lifetime parameter,
//! which can be then combined in tuples to accommodate types with multiple (or
//! zero) lifetime parameters. This type plays several key roles in the safety
//! and flexibility of this crate's functionality, as will be discussed below.
//!
//! Implementing this trait for a type, either manually or by using the included
//! [derive macro][macro@Transient], is the key ingredient to utilizing the
//! functionality of this crate and is discussed in-depth in
//! [its documentation][Transient].
//!
//! ## The `Transience` trait
//! In common language, **transience** is a noun that can be defined as
//! [*the quality or state of being transient*]. The `transient` crate adopts this
//! term throughout its code and documentation to describe the relationship that a
//! data structure has with the 0 or more lifetimes parameters it depends on, as
//! codified by the [`Transience`] trait. More specifically, this therm refers the
//! [variance] of a type with respect to each of its generic lifetime parameters,
//! which is a fairly niche topic in everyday Rust programming by plays a major
//! role in the implementation of this crate.
//!
//! ### Transience bounds and transitions
//! A simplified version of this crate's functionality could be implemented by
//! simply allowing a type `T: Transient` to be cast to, and restored from, a
//! `dyn Any<T::Transient>` trait object. This would be sufficient in some cases,
//! but is has a significant limitation in that two types `S` and `T` with
//! differing `Transience` types would erase to different trait objects; the
//! erased types `dyn Any<S::Transience>` and `dyn Any<T::Transience>` would
//! be distinct types that could not be used interchangeably or stored together
//! in a homogeneous container.
//!
//! To evade this limitation and provide maximum flexibility, the `transient::Any`
//! trait has a bounded blanket implementation that allows a type to erase to
//! any transience which is more (or equally) conservative than its own. For
//! example, a type `struct S<'long>(&'long i32)` that implements `Transient`
//! with a `Transience` of `Co<'long>` can be erased to `dyn Any<_>` with any
//! compatible transience such as `Co<'long>`, `Co<'short>`, `Inv<'long>`, and
//! `Inv<'short>`.
//!
//! #### Mixing _covariant_ and _contravariant_ types
//! As a result of the flexibility discussed above, the following example of
//! storing covariant and contravariant types in the same `Vec` is possible:
//! ```
//! use transient::*;
//!
//! struct CoStruct<'a>(&'a i32);
//! unsafe impl<'a> Transient for CoStruct<'a> {
//! type Static = CoStruct<'static>;
//! type Transience = Co<'a>;
//! }
//!
//! struct ContraStruct<'a>(fn(&'a i32));
//! unsafe impl<'a> Transient for ContraStruct<'a> {
//! type Static = ContraStruct<'static>;
//! type Transience = Contra<'a>;
//! }
//!
//! let value: i32 = 5;
//! fn func(val: &i32) { dbg!(val); }
//!
//! // `co` could erase to `dyn Any<Co>`, but also `dyn Any<Inv>`
//! let co = Box::new(CoStruct(&value));
//! // `co` could erase to `dyn Any<Contra>`, but also `dyn Any<Inv>`
//! let contra = Box::new(ContraStruct(func));
//! // the type annotation coerces both to choose the latter
//! let erased_vec: Vec<Box<dyn Any<Inv>>> = vec![co, contra];
//!
//! assert!(erased_vec[0].downcast_ref::<CoStruct>().is_some());
//! assert!(erased_vec[1].downcast_ref::<ContraStruct>().is_some());
//! ```
//! Note however, that this technique is not _always_ possible; if you have a
//! `CoStruct<'short>` and `ContraStruct<'long>`, there would be no common
//! `Transience` for them to erase to; the first _cannot_ be lengthened to the
//! `'long` lifetime due to its covariance, and the second _cannot_ be shortened
//! to `'short` due to its contravariance.
//!
//! #### Mixing types with different numbers of lifetime parameters
//! Type with more than one lifetime parameter can use a tuple containing a
//! variance item for each lifetime as their `Transience`; this is discussed
//! in-depth in the documentation for the [`Transience`] trait. Consider the
//! following example defining two types, the first with a single lifetime
//! and the second with two. We will choose _invariance_ for all lifetime
//! parameters for simplicity, but when it comes to usage, a similar
//! situation to the "mixed co- and contra-variance " example above arises;
//! if we want to use the types together, we need to find a way to erase
//! both types to a common `dyn Any<_>` trait object:
//! ```
//! use transient::*;
//!
//! struct OneLifetime<'a>(&'a i32);
//! unsafe impl<'a> Transient for OneLifetime<'a> {
//! type Static = OneLifetime<'static>;
//! type Transience = Inv<'a>;
//! }
//!
//! struct TwoLifetimes<'a, 'b>(&'a i32, &'b i32);
//! unsafe impl<'a, 'b> Transient for TwoLifetimes<'a, 'b> {
//! type Static = TwoLifetimes<'static, 'static>;
//! // we use a tuple for the `Transience` that covers both lifetimes
//! type Transience = (Inv<'a>, Inv<'b>);
//! }
//!
//! let (value1, value2) = (5, 7);
//! // The "natural" choice would be erasing to `dyn Any<Inv>`
//! let one = Box::new(OneLifetime(&value1));
//! // The "natural" choice would be erasing to `dyn Any<(Inv, Inv)>`
//! let two = Box::new(TwoLifetimes(&value1, &value2));
//! // The trait objects would not be compatible, but `one` can actually erase
//! // to `dyn Any<(Inv, Inv)>` as well, since adding additional components is
//! // allowed; so let's do that:
//! let erased_vec: Vec<Box<dyn Any<(Inv, Inv)>>> = vec![one, two];
//! assert!(erased_vec[0].downcast_ref::<OneLifetime>().is_some());
//! assert!(erased_vec[1].downcast_ref::<TwoLifetimes>().is_some());
//! ```
//!
//! In this example, we actually could have taken the opposite approach instead
//! by coercing the types to `dyn Any<Inv<'a>>`, which would implicitly force `'a`
//! and `'b` to be equal. In this case that would work since 'a and 'b are indeed
//! equal:
//! ```
//! # use transient::*;
//! # struct OneLifetime<'a>(&'a i32);
//! # unsafe impl<'a> Transient for OneLifetime<'a> {
//! # type Static = OneLifetime<'static>;
//! # type Transience = Inv<'a>;
//! # }
//! # struct TwoLifetimes<'a, 'b>(&'a i32, &'b i32);
//! # unsafe impl<'a, 'b> Transient for TwoLifetimes<'a, 'b> {
//! # type Static = TwoLifetimes<'static, 'static>;
//! # type Transience = (Inv<'a>, Inv<'b>);
//! # }
//! let (value1, value2) = (5, 7);
//! let one = Box::new(OneLifetime(&value1));
//! let two = Box::new(TwoLifetimes(&value1, &value2));
//! // allowed because 'a == 'b
//! let erased_vec: Vec<Box<dyn Any<Inv>>> = vec![one, two];
//! assert!(erased_vec[0].downcast_ref::<OneLifetime>().is_some());
//! assert!(erased_vec[1].downcast_ref::<TwoLifetimes>().is_some());
//! ```
//! However if `'a `was `'short` and `'b` was `'long` then the invariance would
//! prevent them from being unified.
//!
//! [`TypeId`]: TypeId
//! [`transient::Any`]: Any
//! [`Transient`]: tr::Transient
//! [variance]: https://doc.rust-lang.org/nomicon/subtyping.html
//! [_subtyping and variance_]: https://doc.rust-lang.org/nomicon/subtyping.html
//! [*the quality or state of being transient*]: https://www.merriam-webster.com/dictionary/transience
#![deny(missing_docs, clippy::missing_safety_doc)]
#[cfg(test)]
pub mod tests;
pub mod any;
pub mod transience;
// intentionally non-public to avoid naming conflicts with the crate
mod transient;
#[doc(inline)]
pub use crate::any::{Any, TypeId};
#[doc(inline)]
pub use crate::transient::Transient;
#[doc(inline)]
pub use transience::{Co, Contra, Inv, Timeless, Transience};
pub use transience::{CanRecoverFrom, CanTranscendTo};
pub use crate::any::{Downcast, Transcend};
#[cfg(feature = "derive")]
pub use transient_derive::Transient;
/// Re-exports the [`Transient`] trait to enable unambiguously importing it
/// instead of the [`Transient`][transient_derive::Transient] derive macro.
pub mod tr {
pub use super::transient::Transient;
}