├── derive ├── LICENSE-MIT ├── LICENSE-APACHE ├── tests │ ├── fail │ │ ├── 02-unrecognized-lifetime.stderr │ │ ├── 01-too-many-lifetimes.stderr │ │ ├── 05-co-declared-contra.rs │ │ ├── 03-contra-declared-co.rs │ │ ├── 04-inv-declared-co.rs │ │ ├── 06-inv-declared-contra.rs │ │ ├── 02-unrecognized-lifetime.rs │ │ ├── 01-too-many-lifetimes.rs │ │ ├── 07-duplicate-variances.rs │ │ ├── 07-duplicate-variances.stderr │ │ ├── 03-contra-declared-co.stderr │ │ ├── 05-co-declared-contra.stderr │ │ ├── 04-inv-declared-co.stderr │ │ └── 06-inv-declared-contra.stderr │ ├── expand │ │ ├── 05-alternate-crate.rs │ │ ├── 04-mixed-variance.rs │ │ ├── 01-no-attributes.rs │ │ ├── 03-contravariance.rs │ │ ├── 02-covariance.rs │ │ ├── 01-no-attributes.expanded.rs │ │ ├── 05-alternate-crate.expanded.rs │ │ ├── 04-mixed-variance.expanded.rs │ │ ├── 03-contravariance.expanded.rs │ │ └── 02-covariance.expanded.rs │ ├── test_derive.rs │ └── pass │ │ ├── 07-alternate-crate.rs │ │ ├── 05-covariant.rs │ │ ├── 01-no-type-params.rs │ │ ├── 04-no-lifetime.rs │ │ ├── 02-one-type-param.rs │ │ ├── 03-two-type-params.rs │ │ └── 06-contravariant.rs ├── Cargo.toml └── src │ ├── options.rs │ ├── utils.rs │ ├── checks.rs │ ├── variance.rs │ └── lib.rs ├── .rustfmt.toml ├── .gitignore ├── tests ├── test_compiler_error.rs └── fail │ ├── mixed-contravariance-misuse.stderr │ ├── mixed-covariance-misuse.stderr │ ├── invariance-bad-function.rs │ ├── mixed-covariance-misuse.rs │ ├── mixed-contravariance-misuse.rs │ └── invariance-bad-function.stderr ├── LICENSE-MIT ├── Cargo.toml ├── .github └── workflows │ └── ci.yml ├── src ├── tests.rs ├── lib.rs ├── any.rs ├── transient.rs └── transience.rs ├── README.md └── LICENSE-APACHE /derive/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /.rustfmt.toml: -------------------------------------------------------------------------------- 1 | struct_lit_width = 40 -------------------------------------------------------------------------------- /derive/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # cargo 3 | Cargo.lock 4 | .cargo 5 | 6 | # build artifacts 7 | target 8 | build 9 | 10 | # IDE configs 11 | .idea 12 | .vscode 13 | 14 | # trybuild outputs 15 | wip 16 | derive/wip 17 | -------------------------------------------------------------------------------- /derive/tests/fail/02-unrecognized-lifetime.stderr: -------------------------------------------------------------------------------- 1 | error: Variance declared for an invalid lifetime `'a`! 2 | --> tests/fail/02-unrecognized-lifetime.rs:5:13 3 | | 4 | 5 | #[covariant(a)] 5 | | ^ 6 | -------------------------------------------------------------------------------- /derive/tests/fail/01-too-many-lifetimes.stderr: -------------------------------------------------------------------------------- 1 | error: At most 5 lifetime parameters are allowed! 2 | --> tests/fail/01-too-many-lifetimes.rs:5:30 3 | | 4 | 5 | struct S<'a, 'b, 'c, 'd, 'e, 'f, T> { 5 | | ^^ 6 | -------------------------------------------------------------------------------- /derive/tests/fail/05-co-declared-contra.rs: -------------------------------------------------------------------------------- 1 | use transient::Transient; 2 | 3 | #[derive(Transient)] 4 | #[contravariant(a)] 5 | struct CovariantType<'a, T> { 6 | value: &'a T, 7 | } 8 | 9 | fn main() { 10 | // this test should fail to compile 11 | } 12 | -------------------------------------------------------------------------------- /derive/tests/fail/03-contra-declared-co.rs: -------------------------------------------------------------------------------- 1 | use transient::Transient; 2 | 3 | #[derive(Transient)] 4 | #[covariant(a)] 5 | struct ContravariantType<'a, T> { 6 | value: fn(&'a T), 7 | } 8 | 9 | fn main() { 10 | // this test should fail to compile 11 | } 12 | -------------------------------------------------------------------------------- /derive/tests/fail/04-inv-declared-co.rs: -------------------------------------------------------------------------------- 1 | use transient::Transient; 2 | 3 | #[derive(Transient)] 4 | #[covariant(a)] 5 | struct InvariantType<'a, T> { 6 | value: fn(&'a T) -> &'a T, 7 | } 8 | 9 | fn main() { 10 | // this test should fail to compile 11 | } 12 | -------------------------------------------------------------------------------- /derive/tests/fail/06-inv-declared-contra.rs: -------------------------------------------------------------------------------- 1 | use transient::Transient; 2 | 3 | #[derive(Transient)] 4 | #[contravariant(a)] 5 | struct InvariantType<'a, T> { 6 | value: fn(&'a T) -> &'a T, 7 | } 8 | 9 | fn main() { 10 | // this test should fail to compile 11 | } 12 | -------------------------------------------------------------------------------- /tests/test_compiler_error.rs: -------------------------------------------------------------------------------- 1 | //! Entry-point for tests that should fail to compile with an expected error message 2 | 3 | #[rustversion::all(since(1.82), stable)] 4 | #[test] 5 | #[cfg_attr(miri, ignore)] 6 | fn variance_tests() { 7 | let t = trybuild::TestCases::new(); 8 | t.compile_fail("tests/fail/*.rs"); 9 | } 10 | -------------------------------------------------------------------------------- /derive/tests/expand/05-alternate-crate.rs: -------------------------------------------------------------------------------- 1 | //! Verifies that the mixed variance attributes expands as expected 2 | use transient_derive::Transient; 3 | 4 | #[derive(Transient)] 5 | #[transient(crate = mycrate::transient)] 6 | #[contravariant(a)] 7 | #[covariant(b)] 8 | struct ContraCo<'a, 'b, T> { 9 | value1: fn(&'a T) -> &'b str, 10 | } 11 | -------------------------------------------------------------------------------- /derive/tests/fail/02-unrecognized-lifetime.rs: -------------------------------------------------------------------------------- 1 | //! Ensure fails when the `covariant` attribute is used on a `'static` struct 2 | use transient::Transient; 3 | 4 | #[derive(Debug, Clone, PartialEq, Eq, Transient)] 5 | #[covariant(a)] 6 | struct S { 7 | value1: i32, 8 | value2: String, 9 | } 10 | 11 | fn main() { 12 | // this test should fail to compile 13 | } 14 | -------------------------------------------------------------------------------- /derive/tests/test_derive.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn test_pass() { 3 | let t = trybuild::TestCases::new(); 4 | t.pass("tests/pass/*.rs"); 5 | } 6 | 7 | #[rustversion::all(since(1.82), stable)] 8 | #[test] 9 | fn test_fail() { 10 | let t = trybuild::TestCases::new(); 11 | t.compile_fail("tests/fail/*.rs"); 12 | } 13 | 14 | #[test] 15 | fn test_expand() { 16 | macrotest::expand("tests/expand/*.rs"); 17 | } 18 | -------------------------------------------------------------------------------- /derive/tests/pass/07-alternate-crate.rs: -------------------------------------------------------------------------------- 1 | //! Verifies that the mixed variance attributes expands as expected 2 | use transient_derive::Transient; 3 | 4 | mod mycrate { 5 | pub use transient; 6 | } 7 | 8 | #[derive(Transient)] 9 | #[transient(crate = mycrate::transient)] 10 | #[contravariant(a)] 11 | #[covariant(b)] 12 | struct ContraCo<'a, 'b, T> { 13 | value1: fn(&'a T) -> &'b str, 14 | } 15 | 16 | fn main() {} 17 | -------------------------------------------------------------------------------- /derive/tests/fail/01-too-many-lifetimes.rs: -------------------------------------------------------------------------------- 1 | //! Tests the behavior when used on structs with too many lifetime parameters 2 | use transient::Transient; 3 | 4 | #[derive(Debug, Clone, PartialEq, Eq, Transient)] 5 | struct S<'a, 'b, 'c, 'd, 'e, 'f, T> { 6 | value1: &'a T, 7 | value2: &'b T, 8 | value3: &'c T, 9 | value4: &'d T, 10 | value5: &'e T, 11 | value6: &'f T, 12 | } 13 | 14 | fn main() { 15 | // this test should fail to compile 16 | } 17 | -------------------------------------------------------------------------------- /tests/fail/mixed-contravariance-misuse.stderr: -------------------------------------------------------------------------------- 1 | error: lifetime may not live long enough 2 | --> tests/fail/mixed-contravariance-misuse.rs:16:5 3 | | 4 | 13 | fn shorten<'b, 'short, 'long: 'short>( 5 | | ------ ----- lifetime `'long` defined here 6 | | | 7 | | lifetime `'short` defined here 8 | ... 9 | 16 | long_long 10 | | ^^^^^^^^^ coercion requires that `'short` must outlive `'long` 11 | | 12 | = help: consider adding the following bound: `'short: 'long` 13 | -------------------------------------------------------------------------------- /derive/tests/expand/04-mixed-variance.rs: -------------------------------------------------------------------------------- 1 | //! Verifies that the mixed variance attributes expands as expected 2 | use transient_derive::Transient; 3 | 4 | #[derive(Transient)] 5 | #[contravariant(a)] 6 | #[covariant(b)] 7 | struct ContraCo<'a, 'b, T> { 8 | value1: fn(&'a T) -> &'b str, 9 | } 10 | 11 | #[derive(Transient)] 12 | #[covariant(a)] 13 | struct CoInv<'a, 'b, T1, T2> { 14 | value1: &'a T1, 15 | value2: *mut T2, 16 | } 17 | 18 | #[derive(Transient)] 19 | #[covariant] 20 | struct GlobalCo<'a, 'b, T1, T2> { 21 | value1: &'a T1, 22 | value2: &'b T2, 23 | } 24 | -------------------------------------------------------------------------------- /tests/fail/mixed-covariance-misuse.stderr: -------------------------------------------------------------------------------- 1 | error: lifetime may not live long enough 2 | --> tests/fail/mixed-covariance-misuse.rs:16:5 3 | | 4 | 13 | fn lengthen<'b, 'short, 'long: 'short>( 5 | | ------ ----- lifetime `'long` defined here 6 | | | 7 | | lifetime `'short` defined here 8 | ... 9 | 16 | short_short 10 | | ^^^^^^^^^^^ function was supposed to return data with lifetime `'long` but it is returning data with lifetime `'short` 11 | | 12 | = help: consider adding the following bound: `'short: 'long` 13 | -------------------------------------------------------------------------------- /derive/tests/fail/07-duplicate-variances.rs: -------------------------------------------------------------------------------- 1 | use transient::Transient; 2 | 3 | #[derive(Transient)] 4 | #[covariant(a)] 5 | #[contravariant(a)] 6 | struct CoReplacedByContra<'a, 'b, T> { 7 | value: fn(&'a T) -> &'b T 8 | } 9 | 10 | #[derive(Transient)] 11 | #[covariant] 12 | #[contravariant(a)] 13 | struct GlobalCoReplacedByContra<'a, 'b, T> { 14 | value: fn(&'a T) -> &'b T 15 | } 16 | 17 | #[derive(Transient)] 18 | #[covariant(a, a)] 19 | struct CoDeclaredTwice<'a, 'b, T> { 20 | value1: &'a T, 21 | value2: &'b T, 22 | } 23 | 24 | fn main() { 25 | // this test should fail to compile 26 | } 27 | -------------------------------------------------------------------------------- /derive/tests/expand/01-no-attributes.rs: -------------------------------------------------------------------------------- 1 | //! Verifies that the macro expands as expected with no attributes 2 | use transient_derive::Transient; 3 | 4 | #[derive(Transient)] 5 | struct NoGenerics { 6 | value1: String, 7 | } 8 | 9 | #[derive(Transient)] 10 | struct LifetimeOnly<'a> { 11 | value1: &'a str, 12 | } 13 | 14 | #[derive(Transient)] 15 | struct TypeOnly { 16 | value: T, 17 | } 18 | 19 | #[derive(Transient)] 20 | struct TypeAndLifetime<'a, T> { 21 | value: &'a T, 22 | } 23 | 24 | #[derive(Transient)] 25 | struct TypesAndLifetime<'a, T1, T2: 'static> { 26 | value1: &'a T1, 27 | value2: T2, 28 | } 29 | -------------------------------------------------------------------------------- /derive/tests/fail/07-duplicate-variances.stderr: -------------------------------------------------------------------------------- 1 | error: Duplicate variance specification! 'covariant' replaced by 'contravariant' 2 | --> tests/fail/07-duplicate-variances.rs:5:3 3 | | 4 | 5 | #[contravariant(a)] 5 | | ^^^^^^^^^^^^^ 6 | 7 | error: Duplicate variance specification! 'covariant' replaced by 'contravariant' 8 | --> tests/fail/07-duplicate-variances.rs:12:3 9 | | 10 | 12 | #[contravariant(a)] 11 | | ^^^^^^^^^^^^^ 12 | 13 | error: Duplicate variance specification! 'covariant' replaced by 'covariant' 14 | --> tests/fail/07-duplicate-variances.rs:18:3 15 | | 16 | 18 | #[covariant(a, a)] 17 | | ^^^^^^^^^ 18 | -------------------------------------------------------------------------------- /tests/fail/invariance-bad-function.rs: -------------------------------------------------------------------------------- 1 | //! Ensures that a function signature requiring covariant use of an invariant 2 | //! wrapper gets rejected 3 | use transient::*; 4 | 5 | struct S<'a>(fn(&'a ()) -> &'a ()); 6 | 7 | unsafe impl<'a> Transient for S<'a> { 8 | type Static = S<'static>; 9 | type Transience = Inv<'a>; 10 | } 11 | 12 | // This function requires `long` to shorten its lifetime from 'b to 'a, and 13 | // should be *rejected* if the type is considered to be *invariant*. 14 | fn shrink<'a, 'b: 'a>(long: Box>) -> Box> + 'b> { 15 | long 16 | } 17 | 18 | fn main() { 19 | // This test should fail to compile 20 | } 21 | -------------------------------------------------------------------------------- /tests/fail/mixed-covariance-misuse.rs: -------------------------------------------------------------------------------- 1 | use transient::*; 2 | 3 | struct S<'a, 'b>(fn(&'a usize) -> &'b str); 4 | 5 | unsafe impl<'a, 'b> Transient for S<'a, 'b> { 6 | type Static = S<'static, 'static>; 7 | type Transience = (Contra<'a>, Co<'b>); 8 | } 9 | 10 | // This function requires the *covariant* second lifetime parameter 11 | // to lengthen from 'short to 'long, which should be *rejected* by 12 | // the borrow checker. 13 | fn lengthen<'b, 'short, 'long: 'short>( 14 | short_short: &'b S<'short, 'short>, 15 | ) -> &'b dyn Any<(Contra<'short>, Co<'long>)> { 16 | short_short 17 | } 18 | 19 | fn main() { 20 | // this test should fail to compile 21 | } 22 | -------------------------------------------------------------------------------- /tests/fail/mixed-contravariance-misuse.rs: -------------------------------------------------------------------------------- 1 | use transient::*; 2 | 3 | struct S<'a, 'b>(fn(&'a usize) -> &'b str); 4 | 5 | unsafe impl<'a, 'b> Transient for S<'a, 'b> { 6 | type Static = S<'static, 'static>; 7 | type Transience = (Contra<'a>, Co<'b>); 8 | } 9 | 10 | // This function requires the *contravariant* first lifetime parameter 11 | // to shorten from 'long to 'short, which should be *rejected* by the 12 | // borrow checker. 13 | fn shorten<'b, 'short, 'long: 'short>( 14 | long_long: &'b S<'long, 'short>, 15 | ) -> &'b dyn Any<(Contra<'short>, Co<'short>)> { 16 | long_long 17 | } 18 | 19 | fn main() { 20 | // this test should fail to compile 21 | } 22 | -------------------------------------------------------------------------------- /derive/tests/fail/03-contra-declared-co.stderr: -------------------------------------------------------------------------------- 1 | error: lifetime may not live long enough 2 | --> tests/fail/03-contra-declared-co.rs:3:10 3 | | 4 | 3 | #[derive(Transient)] 5 | | ^^^^^^^^^ function was supposed to return data with lifetime `'__long` but it is returning data with lifetime `'a` 6 | 4 | #[covariant(a)] 7 | | --------- lifetime `'__long` defined here 8 | 5 | struct ContravariantType<'a, T> { 9 | | -- lifetime `'a` defined here 10 | | 11 | = help: consider adding the following bound: `'a: '__long` 12 | = note: this error originates in the derive macro `Transient` (in Nightly builds, run with -Z macro-backtrace for more info) 13 | -------------------------------------------------------------------------------- /derive/tests/fail/05-co-declared-contra.stderr: -------------------------------------------------------------------------------- 1 | error: lifetime may not live long enough 2 | --> tests/fail/05-co-declared-contra.rs:3:10 3 | | 4 | 3 | #[derive(Transient)] 5 | | ^^^^^^^^^ function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'__short` 6 | 4 | #[contravariant(a)] 7 | | ------------- lifetime `'__short` defined here 8 | 5 | struct CovariantType<'a, T> { 9 | | -- lifetime `'a` defined here 10 | | 11 | = help: consider adding the following bound: `'__short: 'a` 12 | = note: this error originates in the derive macro `Transient` (in Nightly builds, run with -Z macro-backtrace for more info) 13 | -------------------------------------------------------------------------------- /derive/tests/expand/03-contravariance.rs: -------------------------------------------------------------------------------- 1 | //! Verifies that the `covariant` attribute expands as expected 2 | use transient_derive::Transient; 3 | 4 | #[derive(Transient)] 5 | #[contravariant(a)] 6 | struct LifetimeOnly<'a> { 7 | value1: fn(&'a str), 8 | } 9 | 10 | #[derive(Transient)] 11 | #[contravariant(a)] 12 | struct TypeAndLifetime<'a, T> { 13 | value: fn(&'a T), 14 | } 15 | 16 | #[derive(Transient)] 17 | #[contravariant(a)] 18 | struct TypesAndLifetime<'a, T1, T2> { 19 | value1: fn(&'a T1) -> T2, 20 | } 21 | 22 | #[derive(Transient)] 23 | #[contravariant(a, b)] 24 | struct TypesAndTwoLifetimes<'a, 'b, T1, T2> { 25 | value1: fn(&'a T1), 26 | value2: fn(&'a T2), 27 | } 28 | -------------------------------------------------------------------------------- /derive/tests/pass/05-covariant.rs: -------------------------------------------------------------------------------- 1 | //! Tests the behavior when used on structs with no type parameters 2 | use transient::{Any, Co, Downcast, Inv, Transient}; 3 | 4 | #[derive(Debug, Clone, PartialEq, Eq, Transient)] 5 | #[covariant(a)] 6 | struct SS<'a> { 7 | value: &'a String, 8 | } 9 | 10 | #[derive(Debug, Clone, PartialEq, Eq, Transient)] 11 | #[covariant(a)] 12 | struct _SS<'a> { 13 | value: &'a String, 14 | } 15 | 16 | fn main() { 17 | let string = "qwer".to_string(); 18 | let original = SS { value: &string }; 19 | let inv_erased = &original as &dyn Any; 20 | assert_eq!(inv_erased.downcast_ref::(), Some(&original)); 21 | 22 | let co_erased = &original as &dyn Any; 23 | assert_eq!(co_erased.downcast_ref::(), Some(&original)); 24 | } 25 | -------------------------------------------------------------------------------- /derive/tests/expand/02-covariance.rs: -------------------------------------------------------------------------------- 1 | //! Verifies that the `covariant` attribute expands as expected 2 | use transient_derive::Transient; 3 | 4 | #[derive(Transient)] 5 | struct NoGenerics { 6 | value1: String, 7 | } 8 | 9 | #[derive(Transient)] 10 | struct TypeOnly { 11 | value: T, 12 | } 13 | 14 | #[derive(Transient)] 15 | #[covariant(a)] 16 | struct LifetimeOnly<'a> { 17 | value1: &'a str, 18 | } 19 | 20 | #[derive(Transient)] 21 | #[covariant(a)] 22 | struct TypeAndLifetime<'a, T> { 23 | value: &'a T, 24 | } 25 | 26 | #[derive(Transient)] 27 | #[covariant(a)] 28 | struct TypesAndLifetime<'a, T1, T2> { 29 | value1: &'a T1, 30 | value2: T2, 31 | } 32 | 33 | #[derive(Transient)] 34 | #[covariant(a, b)] 35 | struct TypesAndTwoLifetimes<'a, 'b, T1, T2: 'static> { 36 | value1: &'a T1, 37 | value2: &'a T2, 38 | } 39 | -------------------------------------------------------------------------------- /tests/fail/invariance-bad-function.stderr: -------------------------------------------------------------------------------- 1 | error: lifetime may not live long enough 2 | --> tests/fail/invariance-bad-function.rs:15:5 3 | | 4 | 14 | fn shrink<'a, 'b: 'a>(long: Box>) -> Box> + 'b> { 5 | | -- -- lifetime `'b` defined here 6 | | | 7 | | lifetime `'a` defined here 8 | 15 | long 9 | | ^^^^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a` 10 | | 11 | = help: consider adding the following bound: `'a: 'b` 12 | = note: requirement occurs because of the type `transient::Inv<'_>`, which makes the generic argument `'_` invariant 13 | = note: the struct `transient::Inv<'a>` is invariant over the parameter `'a` 14 | = help: see for more information about variance 15 | -------------------------------------------------------------------------------- /derive/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "transient-derive" 3 | version = "0.4.1" 4 | description = "Proc macro for deriving the `transient::Transient` trait" 5 | authors = ["Joshua Rudolph "] 6 | keywords = ["any", "static", "downcast", "typeid"] 7 | homepage = "https://github.com/JRRudy1/transient" 8 | repository = "https://github.com/JRRudy1/transient" 9 | documentation = "https://docs.rs/transient" 10 | license = "MIT OR Apache-2.0" 11 | edition = "2021" 12 | 13 | [lib] 14 | proc-macro = true 15 | doctest = true 16 | 17 | [[test]] 18 | name = "tests" 19 | path = "tests/test_derive.rs" 20 | 21 | [dependencies] 22 | proc-macro2 = { version = "1", default-features = false } 23 | quote = "1" 24 | syn = { version = "2", features = ["full", "extra-traits"] } 25 | thiserror = "1" 26 | 27 | [dev-dependencies] 28 | trybuild = { version = "1.0.96", features = ["diff"] } 29 | macrotest = "1.0.12" 30 | rustversion = "1.0" 31 | transient = { path = ".." } 32 | -------------------------------------------------------------------------------- /derive/tests/fail/04-inv-declared-co.stderr: -------------------------------------------------------------------------------- 1 | error: lifetime may not live long enough 2 | --> tests/fail/04-inv-declared-co.rs:3:10 3 | | 4 | 3 | #[derive(Transient)] 5 | | ^^^^^^^^^ function was supposed to return data with lifetime `'__long` but it is returning data with lifetime `'a` 6 | 4 | #[covariant(a)] 7 | | --------- lifetime `'__long` defined here 8 | 5 | struct InvariantType<'a, T> { 9 | | -- lifetime `'a` defined here 10 | | 11 | = help: consider adding the following bound: `'a: '__long` 12 | = note: requirement occurs because of the type `InvariantType<'_, T>`, which makes the generic argument `'_` invariant 13 | = note: the struct `InvariantType<'a, T>` is invariant over the parameter `'a` 14 | = help: see for more information about variance 15 | = note: this error originates in the derive macro `Transient` (in Nightly builds, run with -Z macro-backtrace for more info) 16 | -------------------------------------------------------------------------------- /derive/tests/fail/06-inv-declared-contra.stderr: -------------------------------------------------------------------------------- 1 | error: lifetime may not live long enough 2 | --> tests/fail/06-inv-declared-contra.rs:3:10 3 | | 4 | 3 | #[derive(Transient)] 5 | | ^^^^^^^^^ function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'__short` 6 | 4 | #[contravariant(a)] 7 | | ------------- lifetime `'__short` defined here 8 | 5 | struct InvariantType<'a, T> { 9 | | -- lifetime `'a` defined here 10 | | 11 | = help: consider adding the following bound: `'__short: 'a` 12 | = note: requirement occurs because of the type `InvariantType<'_, T>`, which makes the generic argument `'_` invariant 13 | = note: the struct `InvariantType<'a, T>` is invariant over the parameter `'a` 14 | = help: see for more information about variance 15 | = note: this error originates in the derive macro `Transient` (in Nightly builds, run with -Z macro-backtrace for more info) 16 | -------------------------------------------------------------------------------- /derive/tests/pass/01-no-type-params.rs: -------------------------------------------------------------------------------- 1 | //! Tests the behavior when used on structs with no type parameters 2 | use transient::{Transient, Downcast}; 3 | 4 | #[derive(Debug, Clone, PartialEq, Eq, Transient)] 5 | struct SS<'a> { 6 | value: &'a String, 7 | } 8 | 9 | fn main() { 10 | let string = "qwer".to_string(); 11 | let mut original = SS{value: &string}; 12 | { // owned 13 | let erased = Box::new(original.clone()).erase(); 14 | assert!(erased.is::()); 15 | let restored = erased.downcast::().unwrap(); 16 | assert_eq!(restored.as_ref(), &original); 17 | } 18 | { // shared ref 19 | let erased = original.erase_ref(); 20 | assert!(erased.is::()); 21 | let restored = erased.downcast_ref::().unwrap(); 22 | assert_eq!(restored, &original); 23 | } 24 | { // mut ref 25 | let erased = original.erase_mut(); 26 | assert!(erased.is::()); 27 | let restored = erased.downcast_mut::().unwrap(); 28 | assert_eq!(restored.value, &string); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /derive/tests/pass/04-no-lifetime.rs: -------------------------------------------------------------------------------- 1 | //! Tests the behavior when used on structs without a lifetime parameter 2 | use transient::{Transient, Downcast}; 3 | 4 | #[derive(Debug, Clone, PartialEq, Eq, Transient)] 5 | struct S { 6 | value: T, 7 | } 8 | 9 | type SS = S; 10 | 11 | fn main() { 12 | let mut original = S{value: "qwer".to_string()}; 13 | { // owned 14 | let erased = Box::new(original.clone()).erase(); 15 | assert!(erased.is::()); 16 | let restored = erased.downcast::().unwrap(); 17 | assert_eq!(restored.as_ref(), &original); 18 | } 19 | { // shared ref 20 | let erased = original.erase_ref(); 21 | assert!(erased.is::()); 22 | let restored = erased.downcast_ref::().unwrap(); 23 | assert_eq!(restored, &original); 24 | } 25 | { // mut ref 26 | let mut cloned = original.clone(); 27 | let erased = cloned.erase_mut(); 28 | assert!(erased.is::()); 29 | let restored = erased.downcast_mut::().unwrap(); 30 | assert_eq!(restored, &mut original); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /derive/tests/pass/02-one-type-param.rs: -------------------------------------------------------------------------------- 1 | //! Tests the behavior when used on structs with one type parameter 2 | use transient::{Transient, Downcast}; 3 | 4 | #[derive(Debug, Clone, PartialEq, Eq, Transient)] 5 | struct S<'a, T> { 6 | value: &'a T, 7 | } 8 | 9 | type SS<'a> = S<'a, String>; 10 | 11 | fn main() { 12 | let string = "qwer".to_string(); 13 | let mut original = SS{value: &string}; 14 | { // owned 15 | let erased = Box::new(original.clone()).erase(); 16 | assert!(erased.is::()); 17 | let restored = erased.downcast::().unwrap(); 18 | assert_eq!(restored.as_ref(), &original); 19 | } 20 | { // shared ref 21 | let erased = original.erase_ref(); 22 | assert!(erased.is::()); 23 | let restored = erased.downcast_ref::().unwrap(); 24 | assert_eq!(restored, &original); 25 | } 26 | { // mut ref 27 | let mut cloned = original.clone(); 28 | let erased = cloned.erase_mut(); 29 | assert!(erased.is::()); 30 | let restored = erased.downcast_mut::().unwrap(); 31 | assert_eq!(restored, &mut original); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2024-present Joshua Rudolph. https://github.com/JRRudy1 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /derive/tests/pass/03-two-type-params.rs: -------------------------------------------------------------------------------- 1 | //! Tests the behavior when used on structs with two type parameters 2 | use transient::{Transient, Downcast}; 3 | 4 | #[derive(Debug, Clone, PartialEq, Eq, Transient)] 5 | struct S<'a, T1, T2> { 6 | borrowed: &'a T1, 7 | owned: T2, 8 | } 9 | 10 | type SS<'a> = S<'a, String, usize>; 11 | 12 | fn main() { 13 | let (string, number) = ("qwer".to_string(), 5_usize); 14 | let mut original: SS = S{borrowed: &string, owned: number}; 15 | { // owned 16 | let erased = Box::new(original.clone()).erase(); 17 | assert!(erased.is::()); 18 | let restored = erased.downcast::().unwrap(); 19 | assert_eq!(restored.as_ref(), &original); 20 | } 21 | { // shared ref 22 | let erased = original.erase_ref(); 23 | assert!(erased.is::()); 24 | let restored = erased.downcast_ref::().unwrap(); 25 | assert_eq!(restored, &original); 26 | } 27 | { // mut ref 28 | let mut cloned = original.clone(); 29 | let erased = cloned.erase_mut(); 30 | assert!(erased.is::()); 31 | let restored = erased.downcast_mut::().unwrap(); 32 | assert_eq!(restored, &mut original); 33 | } 34 | } -------------------------------------------------------------------------------- /derive/tests/expand/01-no-attributes.expanded.rs: -------------------------------------------------------------------------------- 1 | //! Verifies that the macro expands as expected with no attributes 2 | use transient_derive::Transient; 3 | struct NoGenerics { 4 | value1: String, 5 | } 6 | impl ::transient::Static for NoGenerics {} 7 | struct LifetimeOnly<'a> { 8 | value1: &'a str, 9 | } 10 | unsafe impl<'a> ::transient::Transient for LifetimeOnly<'a> { 11 | type Static = LifetimeOnly<'static>; 12 | type Transience = ::transient::Inv<'a>; 13 | } 14 | struct TypeOnly { 15 | value: T, 16 | } 17 | impl ::transient::Static for TypeOnly 18 | where 19 | T: 'static, 20 | {} 21 | struct TypeAndLifetime<'a, T> { 22 | value: &'a T, 23 | } 24 | unsafe impl<'a, T> ::transient::Transient for TypeAndLifetime<'a, T> 25 | where 26 | T: 'static, 27 | { 28 | type Static = TypeAndLifetime<'static, T>; 29 | type Transience = ::transient::Inv<'a>; 30 | } 31 | struct TypesAndLifetime<'a, T1, T2: 'static> { 32 | value1: &'a T1, 33 | value2: T2, 34 | } 35 | unsafe impl<'a, T1, T2: 'static> ::transient::Transient for TypesAndLifetime<'a, T1, T2> 36 | where 37 | T1: 'static, 38 | { 39 | type Static = TypesAndLifetime<'static, T1, T2>; 40 | type Transience = ::transient::Inv<'a>; 41 | } 42 | -------------------------------------------------------------------------------- /derive/tests/expand/05-alternate-crate.expanded.rs: -------------------------------------------------------------------------------- 1 | //! Verifies that the mixed variance attributes expands as expected 2 | use transient_derive::Transient; 3 | #[transient(crate = mycrate::transient)] 4 | #[contravariant(a)] 5 | #[covariant(b)] 6 | struct ContraCo<'a, 'b, T> { 7 | value1: fn(&'a T) -> &'b str, 8 | } 9 | unsafe impl<'a, 'b, T> mycrate::transient::Transient for ContraCo<'a, 'b, T> 10 | where 11 | T: 'static, 12 | { 13 | type Static = ContraCo<'static, 'static, T>; 14 | type Transience = (mycrate::transient::Contra<'a>, mycrate::transient::Co<'b>); 15 | } 16 | const _: () = { 17 | mod validate_ContraCo { 18 | #![allow(non_snake_case, dead_code)] 19 | use super::*; 20 | fn contravariant_wrt_a<'__short, 'a, 'b, T>( 21 | v: ContraCo<'__short, 'b, T>, 22 | ) -> ContraCo<'a, 'b, T> 23 | where 24 | T: 'static, 25 | 'a: '__short, 26 | { 27 | v 28 | } 29 | fn covariant_wrt_b<'__long, 'a, 'b, T>( 30 | v: ContraCo<'a, '__long, T>, 31 | ) -> ContraCo<'a, 'b, T> 32 | where 33 | T: 'static, 34 | '__long: 'b, 35 | { 36 | v 37 | } 38 | } 39 | }; 40 | -------------------------------------------------------------------------------- /derive/src/options.rs: -------------------------------------------------------------------------------- 1 | use syn::parse::{Parse, ParseStream}; 2 | use syn::punctuated::Punctuated; 3 | use syn::{Path, Token}; 4 | 5 | use crate::{Error, Result}; 6 | 7 | enum TransientOption { 8 | Crate(Path), 9 | } 10 | 11 | impl Parse for TransientOption { 12 | fn parse(input: ParseStream<'_>) -> syn::Result { 13 | let lookahead = input.lookahead1(); 14 | if lookahead.peek(Token![crate]) { 15 | let _: Token![crate] = input.parse()?; 16 | let _: Token![=] = input.parse()?; 17 | input.parse().map(Self::Crate) 18 | } else { 19 | Err(Error::InvalidOption(input.span()).into()) 20 | } 21 | } 22 | } 23 | 24 | pub(crate) struct TransientOptions { 25 | pub(crate) krate: syn::Path, 26 | } 27 | 28 | impl TransientOptions { 29 | fn set(&mut self, opt: TransientOption) { 30 | match opt { 31 | TransientOption::Crate(path) => { 32 | self.krate = path; 33 | } 34 | } 35 | } 36 | } 37 | 38 | impl Default for TransientOptions { 39 | fn default() -> Self { 40 | TransientOptions { krate: syn::parse_quote! { ::transient } } 41 | } 42 | } 43 | 44 | impl TransientOptions { 45 | pub(crate) fn extract(attrs: &[syn::Attribute]) -> Result { 46 | let mut options = Self::default(); 47 | let parser = Punctuated::::parse_terminated; 48 | for attr in attrs 49 | .iter() 50 | .filter(|attr| attr.path().is_ident("transient")) 51 | { 52 | for option in attr.parse_args_with(parser)? { 53 | options.set(option) 54 | } 55 | } 56 | Ok(options) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /derive/tests/pass/06-contravariant.rs: -------------------------------------------------------------------------------- 1 | //! Tests the behavior when used on structs with no type parameters 2 | use transient::{Any, Contra, Downcast, Inv, Transient}; 3 | 4 | #[derive(Debug, Transient)] 5 | #[contravariant(a)] 6 | struct FuncWrap<'a> { 7 | func: fn(&'a str) -> &'static str, 8 | } 9 | 10 | #[derive(Debug, Transient)] 11 | #[contravariant(a)] 12 | struct _FuncWrap<'a> { 13 | func: fn(&'a str) -> &'static str, 14 | } 15 | 16 | impl<'a> FuncWrap<'a> { 17 | fn call<'s: 'a>(&self, s: &'s str) -> &'static str { 18 | (self.func)(s) 19 | } 20 | } 21 | 22 | fn use_any_str(_s: &str) -> &'static str { 23 | "unrelated to the arg" 24 | } 25 | 26 | const STATIC_STR: &'static str = "static_str"; 27 | 28 | fn main() { 29 | let temp_string = "temp_str".to_string(); 30 | 31 | // `accepts_any` is `FuncWrap<'any>` 32 | let accepts_any: FuncWrap<'_> = FuncWrap { func: use_any_str }; 33 | 34 | // it can therefore be flexibly called with any string 35 | let _ = accepts_any.call(&temp_string); 36 | let _ = accepts_any.call(STATIC_STR); 37 | 38 | // casting to `dyn std::any::Any` requires it lengthen to `FuncWrap<'static>`, 39 | // so after being downcast it could only be called with a `&'static str` 40 | let erased: &dyn std::any::Any = &accepts_any; 41 | let restored: &FuncWrap<'_> = erased.downcast_ref().unwrap(); 42 | let _ = restored.call(STATIC_STR); // OK 43 | // but the next line would NOT compile: 44 | // let _ = restored.call(&temp_string); // Not OK 45 | 46 | // if we cast to `dyn transient::Any` instead, it can keep its `'any` lifetime 47 | // and keep accepting `&'short str` after being downcast 48 | let erased: &dyn Any = &accepts_any; 49 | let restored: &FuncWrap<'_> = erased.downcast_ref().unwrap(); 50 | let _ = restored.call(STATIC_STR); // OK 51 | let _ = restored.call(&temp_string); // Still OK 52 | 53 | // `Inv` also works since we don't need it to change its lifetime 54 | let erased: &dyn Any = &accepts_any; 55 | let restored: &FuncWrap<'_> = erased.downcast_ref().unwrap(); 56 | let _ = restored.call(STATIC_STR); // OK 57 | let _ = restored.call(&temp_string); // Still OK 58 | } 59 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "transient" 3 | version = "0.4.1" 4 | description = "Reimplementation of `std::any::Any` with support for non-`'static` types" 5 | authors = ["Joshua Rudolph "] 6 | readme = "README.md" 7 | keywords = ["any", "static", "downcast", "typeid"] 8 | homepage = "https://github.com/JRRudy1/transient" 9 | repository = "https://github.com/JRRudy1/transient" 10 | documentation = "https://docs.rs/transient" 11 | license = "MIT OR Apache-2.0" 12 | exclude = ["/.gitignore"] 13 | edition = "2021" 14 | rust-version = "1.60" 15 | 16 | [lib] 17 | doctest = true 18 | 19 | [workspace] 20 | members = ["derive"] 21 | 22 | [dependencies] 23 | rustversion = "1.0" 24 | 25 | transient-derive = { path = "derive", version = "0.4.1", optional = true } 26 | ndarray = { version = "0.15", optional = true } 27 | numpy = { version = "0.21", optional = true } 28 | pyo3 = { version = ">=0.21", optional = true } 29 | uuid = { version = "1", optional = true } 30 | either = { version = ">=1", default-features = false, optional = true } 31 | serde_json = { version = ">=1", default-features = false, optional = true, features = ["std"] } 32 | rmp-serde = { version = ">=1", default-features = false, optional = true } 33 | 34 | [dev-dependencies] 35 | trybuild = { version = "1.0", features = ["diff"] } 36 | 37 | [features] 38 | default = ["std", "derive"] 39 | 40 | # Provide impls for common standard library types like Vec and HashMap. 41 | # Requires a dependency on the Rust standard library. 42 | std = ["alloc"] 43 | 44 | # Provide impls for types in the Rust core allocation and collections library 45 | # including String, Box, Vec, and Cow. This is a subset of std but may 46 | # be enabled without depending on all of std. 47 | alloc = [] 48 | 49 | # Provides a `derive` macro for implementing the `Transient` trait 50 | derive = ["transient-derive"] 51 | 52 | # Provides `Transient` implementations for `ndarray` types 53 | ndarray = ["dep:ndarray"] 54 | 55 | # Provides `Transient` implementations for `pyo3` types 56 | pyo3 = ["dep:pyo3"] 57 | 58 | # Provides `Transient` implementations for `numpy` types 59 | numpy = ["dep:numpy", "pyo3"] 60 | 61 | # Provides `Transient` implementations for `uuid` types 62 | uuid = ["dep:uuid"] 63 | 64 | # Provides `Transient` implementations for `either` types 65 | either = ["dep:either"] 66 | 67 | # Provides `Transient` implementations for `serde_json` types 68 | serde_json = ["dep:serde_json"] 69 | 70 | # Provides `Transient` implementations for `rmp_serde` types 71 | rmp-serde = ["dep:rmp-serde"] 72 | 73 | [workspace.lints.clippy] 74 | let_unit_value = "warn" 75 | manual_assert = "warn" 76 | unnecessary_wraps = "warn" 77 | 78 | [workspace.lints.rust] 79 | elided_lifetimes_in_paths = "warn" 80 | invalid_doc_attributes = "warn" 81 | unused_lifetimes = "warn" 82 | 83 | [workspace.lints.rustdoc] 84 | broken_intra_doc_links = "warn" 85 | bare_urls = "warn" 86 | -------------------------------------------------------------------------------- /derive/tests/expand/04-mixed-variance.expanded.rs: -------------------------------------------------------------------------------- 1 | //! Verifies that the mixed variance attributes expands as expected 2 | use transient_derive::Transient; 3 | #[contravariant(a)] 4 | #[covariant(b)] 5 | struct ContraCo<'a, 'b, T> { 6 | value1: fn(&'a T) -> &'b str, 7 | } 8 | unsafe impl<'a, 'b, T> ::transient::Transient for ContraCo<'a, 'b, T> 9 | where 10 | T: 'static, 11 | { 12 | type Static = ContraCo<'static, 'static, T>; 13 | type Transience = (::transient::Contra<'a>, ::transient::Co<'b>); 14 | } 15 | const _: () = { 16 | mod validate_ContraCo { 17 | #![allow(non_snake_case, dead_code)] 18 | use super::*; 19 | fn contravariant_wrt_a<'__short, 'a, 'b, T>( 20 | v: ContraCo<'__short, 'b, T>, 21 | ) -> ContraCo<'a, 'b, T> 22 | where 23 | T: 'static, 24 | 'a: '__short, 25 | { 26 | v 27 | } 28 | fn covariant_wrt_b<'__long, 'a, 'b, T>( 29 | v: ContraCo<'a, '__long, T>, 30 | ) -> ContraCo<'a, 'b, T> 31 | where 32 | T: 'static, 33 | '__long: 'b, 34 | { 35 | v 36 | } 37 | } 38 | }; 39 | #[covariant(a)] 40 | struct CoInv<'a, 'b, T1, T2> { 41 | value1: &'a T1, 42 | value2: *mut T2, 43 | } 44 | unsafe impl<'a, 'b, T1, T2> ::transient::Transient for CoInv<'a, 'b, T1, T2> 45 | where 46 | T1: 'static, 47 | T2: 'static, 48 | { 49 | type Static = CoInv<'static, 'static, T1, T2>; 50 | type Transience = (::transient::Co<'a>, ::transient::Inv<'b>); 51 | } 52 | const _: () = { 53 | mod validate_CoInv { 54 | #![allow(non_snake_case, dead_code)] 55 | use super::*; 56 | fn covariant_wrt_a<'__long, 'a, 'b, T1, T2>( 57 | v: CoInv<'__long, 'b, T1, T2>, 58 | ) -> CoInv<'a, 'b, T1, T2> 59 | where 60 | T1: 'static, 61 | T2: 'static, 62 | '__long: 'a, 63 | { 64 | v 65 | } 66 | } 67 | }; 68 | #[covariant] 69 | struct GlobalCo<'a, 'b, T1, T2> { 70 | value1: &'a T1, 71 | value2: &'b T2, 72 | } 73 | unsafe impl<'a, 'b, T1, T2> ::transient::Transient for GlobalCo<'a, 'b, T1, T2> 74 | where 75 | T1: 'static, 76 | T2: 'static, 77 | { 78 | type Static = GlobalCo<'static, 'static, T1, T2>; 79 | type Transience = (::transient::Co<'a>, ::transient::Co<'b>); 80 | } 81 | const _: () = { 82 | mod validate_GlobalCo { 83 | #![allow(non_snake_case, dead_code)] 84 | use super::*; 85 | fn covariant_wrt_a<'__long, 'a, 'b, T1, T2>( 86 | v: GlobalCo<'__long, 'b, T1, T2>, 87 | ) -> GlobalCo<'a, 'b, T1, T2> 88 | where 89 | T1: 'static, 90 | T2: 'static, 91 | '__long: 'a, 92 | { 93 | v 94 | } 95 | fn covariant_wrt_b<'__long, 'a, 'b, T1, T2>( 96 | v: GlobalCo<'a, '__long, T1, T2>, 97 | ) -> GlobalCo<'a, 'b, T1, T2> 98 | where 99 | T1: 'static, 100 | T2: 'static, 101 | '__long: 'b, 102 | { 103 | v 104 | } 105 | } 106 | }; 107 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | # Based on https://github.com/actions-rs/meta/blob/master/recipes/matrix.md 2 | 3 | on: [push, pull_request] 4 | 5 | name: Tests 6 | 7 | jobs: 8 | test: 9 | name: Test 10 | runs-on: ubuntu-latest 11 | strategy: 12 | matrix: 13 | rust: 14 | - stable 15 | - beta 16 | - nightly 17 | features: 18 | - --no-default-features 19 | - --features alloc --no-default-features 20 | - --features default 21 | - --all-features 22 | steps: 23 | - name: Checkout sources 24 | uses: actions/checkout@v2 25 | 26 | - name: Install toolchain 27 | uses: actions-rs/toolchain@v1 28 | with: 29 | profile: minimal 30 | toolchain: ${{ matrix.rust }} 31 | override: true 32 | components: rustfmt, clippy 33 | 34 | - name: Build 35 | uses: actions-rs/cargo@v1 36 | with: 37 | command: build 38 | args: ${{ matrix.features }} 39 | 40 | - name: Test 41 | uses: actions-rs/cargo@v1 42 | with: 43 | command: test 44 | args: ${{ matrix.features }} 45 | 46 | - name: Lint 47 | uses: actions-rs/cargo@v1 48 | with: 49 | command: clippy 50 | args: -- -D warnings 51 | 52 | - name: Formatting 53 | uses: actions-rs/cargo@v1 54 | with: 55 | command: fmt 56 | args: --all -- --check 57 | 58 | miri: 59 | name: Miri Test 60 | runs-on: ubuntu-latest 61 | steps: 62 | - name: Checkout sources 63 | uses: actions/checkout@v2 64 | 65 | - name: Install nightly toolchain 66 | uses: actions-rs/toolchain@v1 67 | with: 68 | profile: minimal 69 | toolchain: nightly 70 | override: true 71 | components: miri, rust-src 72 | 73 | - name: Miri Test 74 | uses: actions-rs/cargo@v1 75 | with: 76 | command: miri 77 | args: test --all-features 78 | 79 | build-msrv: 80 | name: Build MSRV 81 | runs-on: ubuntu-latest 82 | steps: 83 | - name: Checkout sources 84 | uses: actions/checkout@v2 85 | 86 | - name: Install stable toolchain 87 | uses: actions-rs/toolchain@v1 88 | with: 89 | profile: minimal 90 | # 1.68 used instead of the true MSRV to avoid long CI runs 91 | toolchain: "1.68" 92 | override: true 93 | 94 | - name: Build 95 | uses: actions-rs/cargo@v1 96 | with: 97 | command: build 98 | args: --all --all-features 99 | env: 100 | CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse 101 | 102 | test-derive: 103 | name: Test Derive Macro 104 | runs-on: ubuntu-latest 105 | steps: 106 | - name: Checkout sources 107 | uses: actions/checkout@v2 108 | 109 | - name: Install nightly toolchain 110 | uses: actions-rs/toolchain@v1 111 | with: 112 | profile: minimal 113 | toolchain: nightly 114 | override: true 115 | 116 | - name: Install cargo-expand 117 | uses: actions-rs/cargo@v1 118 | with: 119 | command: install 120 | args: --locked --version 1.0.65 cargo-expand 121 | 122 | - name: Test 123 | uses: actions-rs/cargo@v1 124 | with: 125 | command: test 126 | args: --package transient-derive -------------------------------------------------------------------------------- /derive/src/utils.rs: -------------------------------------------------------------------------------- 1 | //! Miscellaneous internal types and functions 2 | use proc_macro2::TokenStream; 3 | use quote::{quote, ToTokens}; 4 | use std::collections::BTreeSet; 5 | use syn::punctuated::Iter; 6 | use syn::{GenericParam, Generics, Ident, Type, TypeParam, TypeParamBound}; 7 | 8 | /// Convenience struct grouping the name of a type and its generics 9 | pub(super) struct TypeWithGenerics<'a> { 10 | pub(super) name: &'a Ident, 11 | pub(super) generics: Generics, 12 | } 13 | 14 | impl<'a> ToTokens for TypeWithGenerics<'a> { 15 | fn to_tokens(&self, tokens: &mut TokenStream) { 16 | let TypeWithGenerics { name, generics } = self; 17 | let type_generics = generics.split_for_impl().1; 18 | quote!(#name #type_generics).to_tokens(tokens); 19 | } 20 | } 21 | 22 | /// Extract the non-'static lifetimes parameters from the given generics 23 | pub(super) fn extract_lifetimes(generics: &Generics) -> Vec { 24 | generics 25 | .lifetimes() 26 | .filter(|lt| lt.lifetime.ident != "static") 27 | .map(|lt| lt.lifetime.clone()) 28 | .collect::>() 29 | } 30 | 31 | /// Adds a `'static` bound to the `where` clause of the given generics for each 32 | /// type parameter, skipping those that already have a 'static bound 33 | #[rustfmt::skip] 34 | pub(super) fn insert_static_predicates(generics: &mut Generics) { 35 | // Add where clause if there isn't one and get a mutable reference to it. We 36 | // can't just use the reference returned from the `make_where_clause` call 37 | // since that would hold a mutable borrow on the entire `Generics` struct. 38 | generics.make_where_clause(); 39 | let where_clause = generics.where_clause.as_mut().unwrap(); 40 | // Collect generics type params that do not have a 'static bound 41 | let mut needs_bound = BTreeSet::<&Ident>::new(); 42 | generics.params.iter() 43 | .filter_map(param_as_type) 44 | .filter(|param| !contains_static_bound(param.bounds.iter())) 45 | .for_each(|param| { needs_bound.insert(¶m.ident); }); 46 | // For each type param in the where clause, add a 'static bound if it does 47 | // not already have one in either location 48 | where_clause.predicates.iter_mut() 49 | .filter_map(predicate_as_type) 50 | .map(|pred| (&pred.bounded_ty, &mut pred.bounds)) 51 | .filter_map(|(ty, bounds)| Some((type_as_ident(ty)?, bounds))) 52 | .for_each(|(ident, bounds)| { 53 | if !needs_bound.remove(ident) { return; } 54 | if contains_static_bound(bounds.iter()) { return; } 55 | bounds.push(syn::parse_quote!('static)); 56 | }); 57 | // Add bound for any remaining params that were missed in the last pass since 58 | // they did not have an existing where clause predicate 59 | for ident in &needs_bound { 60 | where_clause.predicates.push(syn::parse_quote!(#ident: 'static)); 61 | } 62 | } 63 | 64 | fn contains_static_bound(mut bounds: Iter) -> bool { 65 | bounds.any(|bnd| matches!(bnd, TypeParamBound::Lifetime(lt) if lt.ident == "static")) 66 | } 67 | 68 | fn param_as_type(param: &GenericParam) -> Option<&TypeParam> { 69 | if let GenericParam::Type(tp) = param { 70 | Some(tp) 71 | } else { 72 | None 73 | } 74 | } 75 | 76 | fn predicate_as_type(pred: &mut syn::WherePredicate) -> Option<&mut syn::PredicateType> { 77 | if let syn::WherePredicate::Type(tp) = pred { 78 | Some(tp) 79 | } else { 80 | None 81 | } 82 | } 83 | 84 | fn type_as_ident(ty: &Type) -> Option<&Ident> { 85 | if let Type::Path(path) = ty { 86 | path.path.get_ident() 87 | } else { 88 | None 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /derive/tests/expand/03-contravariance.expanded.rs: -------------------------------------------------------------------------------- 1 | //! Verifies that the `covariant` attribute expands as expected 2 | use transient_derive::Transient; 3 | #[contravariant(a)] 4 | struct LifetimeOnly<'a> { 5 | value1: fn(&'a str), 6 | } 7 | unsafe impl<'a> ::transient::Transient for LifetimeOnly<'a> { 8 | type Static = LifetimeOnly<'static>; 9 | type Transience = ::transient::Contra<'a>; 10 | } 11 | const _: () = { 12 | mod validate_LifetimeOnly { 13 | #![allow(non_snake_case, dead_code)] 14 | use super::*; 15 | fn contravariant_wrt_a<'__short, 'a>( 16 | v: LifetimeOnly<'__short>, 17 | ) -> LifetimeOnly<'a> 18 | where 19 | 'a: '__short, 20 | { 21 | v 22 | } 23 | } 24 | }; 25 | #[contravariant(a)] 26 | struct TypeAndLifetime<'a, T> { 27 | value: fn(&'a T), 28 | } 29 | unsafe impl<'a, T> ::transient::Transient for TypeAndLifetime<'a, T> 30 | where 31 | T: 'static, 32 | { 33 | type Static = TypeAndLifetime<'static, T>; 34 | type Transience = ::transient::Contra<'a>; 35 | } 36 | const _: () = { 37 | mod validate_TypeAndLifetime { 38 | #![allow(non_snake_case, dead_code)] 39 | use super::*; 40 | fn contravariant_wrt_a<'__short, 'a, T>( 41 | v: TypeAndLifetime<'__short, T>, 42 | ) -> TypeAndLifetime<'a, T> 43 | where 44 | T: 'static, 45 | 'a: '__short, 46 | { 47 | v 48 | } 49 | } 50 | }; 51 | #[contravariant(a)] 52 | struct TypesAndLifetime<'a, T1, T2> { 53 | value1: fn(&'a T1) -> T2, 54 | } 55 | unsafe impl<'a, T1, T2> ::transient::Transient for TypesAndLifetime<'a, T1, T2> 56 | where 57 | T1: 'static, 58 | T2: 'static, 59 | { 60 | type Static = TypesAndLifetime<'static, T1, T2>; 61 | type Transience = ::transient::Contra<'a>; 62 | } 63 | const _: () = { 64 | mod validate_TypesAndLifetime { 65 | #![allow(non_snake_case, dead_code)] 66 | use super::*; 67 | fn contravariant_wrt_a<'__short, 'a, T1, T2>( 68 | v: TypesAndLifetime<'__short, T1, T2>, 69 | ) -> TypesAndLifetime<'a, T1, T2> 70 | where 71 | T1: 'static, 72 | T2: 'static, 73 | 'a: '__short, 74 | { 75 | v 76 | } 77 | } 78 | }; 79 | #[contravariant(a, b)] 80 | struct TypesAndTwoLifetimes<'a, 'b, T1, T2> { 81 | value1: fn(&'a T1), 82 | value2: fn(&'a T2), 83 | } 84 | unsafe impl<'a, 'b, T1, T2> ::transient::Transient 85 | for TypesAndTwoLifetimes<'a, 'b, T1, T2> 86 | where 87 | T1: 'static, 88 | T2: 'static, 89 | { 90 | type Static = TypesAndTwoLifetimes<'static, 'static, T1, T2>; 91 | type Transience = (::transient::Contra<'a>, ::transient::Contra<'b>); 92 | } 93 | const _: () = { 94 | mod validate_TypesAndTwoLifetimes { 95 | #![allow(non_snake_case, dead_code)] 96 | use super::*; 97 | fn contravariant_wrt_a<'__short, 'a, 'b, T1, T2>( 98 | v: TypesAndTwoLifetimes<'__short, 'b, T1, T2>, 99 | ) -> TypesAndTwoLifetimes<'a, 'b, T1, T2> 100 | where 101 | T1: 'static, 102 | T2: 'static, 103 | 'a: '__short, 104 | { 105 | v 106 | } 107 | fn contravariant_wrt_b<'__short, 'a, 'b, T1, T2>( 108 | v: TypesAndTwoLifetimes<'a, '__short, T1, T2>, 109 | ) -> TypesAndTwoLifetimes<'a, 'b, T1, T2> 110 | where 111 | T1: 'static, 112 | T2: 'static, 113 | 'b: '__short, 114 | { 115 | v 116 | } 117 | } 118 | }; 119 | -------------------------------------------------------------------------------- /derive/tests/expand/02-covariance.expanded.rs: -------------------------------------------------------------------------------- 1 | //! Verifies that the `covariant` attribute expands as expected 2 | use transient_derive::Transient; 3 | struct NoGenerics { 4 | value1: String, 5 | } 6 | impl ::transient::Static for NoGenerics {} 7 | struct TypeOnly { 8 | value: T, 9 | } 10 | impl ::transient::Static for TypeOnly 11 | where 12 | T: 'static, 13 | {} 14 | #[covariant(a)] 15 | struct LifetimeOnly<'a> { 16 | value1: &'a str, 17 | } 18 | unsafe impl<'a> ::transient::Transient for LifetimeOnly<'a> { 19 | type Static = LifetimeOnly<'static>; 20 | type Transience = ::transient::Co<'a>; 21 | } 22 | const _: () = { 23 | mod validate_LifetimeOnly { 24 | #![allow(non_snake_case, dead_code)] 25 | use super::*; 26 | fn covariant_wrt_a<'__long, 'a>(v: LifetimeOnly<'__long>) -> LifetimeOnly<'a> 27 | where 28 | '__long: 'a, 29 | { 30 | v 31 | } 32 | } 33 | }; 34 | #[covariant(a)] 35 | struct TypeAndLifetime<'a, T> { 36 | value: &'a T, 37 | } 38 | unsafe impl<'a, T> ::transient::Transient for TypeAndLifetime<'a, T> 39 | where 40 | T: 'static, 41 | { 42 | type Static = TypeAndLifetime<'static, T>; 43 | type Transience = ::transient::Co<'a>; 44 | } 45 | const _: () = { 46 | mod validate_TypeAndLifetime { 47 | #![allow(non_snake_case, dead_code)] 48 | use super::*; 49 | fn covariant_wrt_a<'__long, 'a, T>( 50 | v: TypeAndLifetime<'__long, T>, 51 | ) -> TypeAndLifetime<'a, T> 52 | where 53 | T: 'static, 54 | '__long: 'a, 55 | { 56 | v 57 | } 58 | } 59 | }; 60 | #[covariant(a)] 61 | struct TypesAndLifetime<'a, T1, T2> { 62 | value1: &'a T1, 63 | value2: T2, 64 | } 65 | unsafe impl<'a, T1, T2> ::transient::Transient for TypesAndLifetime<'a, T1, T2> 66 | where 67 | T1: 'static, 68 | T2: 'static, 69 | { 70 | type Static = TypesAndLifetime<'static, T1, T2>; 71 | type Transience = ::transient::Co<'a>; 72 | } 73 | const _: () = { 74 | mod validate_TypesAndLifetime { 75 | #![allow(non_snake_case, dead_code)] 76 | use super::*; 77 | fn covariant_wrt_a<'__long, 'a, T1, T2>( 78 | v: TypesAndLifetime<'__long, T1, T2>, 79 | ) -> TypesAndLifetime<'a, T1, T2> 80 | where 81 | T1: 'static, 82 | T2: 'static, 83 | '__long: 'a, 84 | { 85 | v 86 | } 87 | } 88 | }; 89 | #[covariant(a, b)] 90 | struct TypesAndTwoLifetimes<'a, 'b, T1, T2: 'static> { 91 | value1: &'a T1, 92 | value2: &'a T2, 93 | } 94 | unsafe impl<'a, 'b, T1, T2: 'static> ::transient::Transient 95 | for TypesAndTwoLifetimes<'a, 'b, T1, T2> 96 | where 97 | T1: 'static, 98 | { 99 | type Static = TypesAndTwoLifetimes<'static, 'static, T1, T2>; 100 | type Transience = (::transient::Co<'a>, ::transient::Co<'b>); 101 | } 102 | const _: () = { 103 | mod validate_TypesAndTwoLifetimes { 104 | #![allow(non_snake_case, dead_code)] 105 | use super::*; 106 | fn covariant_wrt_a<'__long, 'a, 'b, T1, T2: 'static>( 107 | v: TypesAndTwoLifetimes<'__long, 'b, T1, T2>, 108 | ) -> TypesAndTwoLifetimes<'a, 'b, T1, T2> 109 | where 110 | T1: 'static, 111 | '__long: 'a, 112 | { 113 | v 114 | } 115 | fn covariant_wrt_b<'__long, 'a, 'b, T1, T2: 'static>( 116 | v: TypesAndTwoLifetimes<'a, '__long, T1, T2>, 117 | ) -> TypesAndTwoLifetimes<'a, 'b, T1, T2> 118 | where 119 | T1: 'static, 120 | '__long: 'b, 121 | { 122 | v 123 | } 124 | } 125 | }; 126 | -------------------------------------------------------------------------------- /derive/src/checks.rs: -------------------------------------------------------------------------------- 1 | //! Defines functionality for generating the validation module ensuring that the 2 | //! variances declared using the macro attributes are valid for the type. 3 | use proc_macro2::TokenStream; 4 | use quote::{quote, ToTokens}; 5 | use syn::{parse_quote, parse_quote_spanned, spanned::Spanned}; 6 | use syn::{GenericParam, Generics, Ident, LifetimeParam, WherePredicate}; 7 | 8 | use super::{SelfType, TransientImpl, TypeWithGenerics, Variance, VarianceKind}; 9 | 10 | /// module generated to force a compile error if an invalid variance is selected 11 | pub(super) struct ChecksModule<'a> { 12 | name: Ident, 13 | funcs: Vec>, 14 | } 15 | 16 | impl<'a> ChecksModule<'a> { 17 | #[rustfmt::skip] 18 | pub(super) fn new(impl_: &'a TransientImpl<'a>) -> Option { 19 | let TransientImpl(self_type, _, transience, _) = impl_; 20 | let name = Ident::new( 21 | &format!("validate_{}", self_type.name), 22 | self_type.name.span(), 23 | ); 24 | let funcs = transience.0.iter() 25 | .filter_map(|variance| CheckFn::new(self_type, variance)) 26 | .collect::>(); 27 | if funcs.is_empty() { 28 | None 29 | } else { 30 | Some(ChecksModule { name, funcs }) 31 | } 32 | } 33 | } 34 | 35 | impl<'a> ToTokens for ChecksModule<'a> { 36 | fn to_tokens(&self, tokens: &mut TokenStream) { 37 | let ChecksModule { name, funcs } = self; 38 | tokens.extend(quote!( 39 | const _: () = { 40 | mod #name { 41 | #![allow(non_snake_case, dead_code)] 42 | use super::*; 43 | #(#funcs)* 44 | } 45 | }; 46 | )); 47 | } 48 | } 49 | 50 | struct CheckFn<'a> { 51 | func_name: Ident, 52 | // name and generics for the function's argument 53 | arg_type: TypeWithGenerics<'a>, 54 | // same as the implementing type 55 | return_type: &'a SelfType, 56 | // generics and where clause for the function 57 | generics: Generics, 58 | } 59 | impl<'a> CheckFn<'a> { 60 | #[allow(clippy::needless_late_init)] 61 | fn new(target_type: &'a SelfType, variance: &'a Variance) -> Option { 62 | let lt = &variance.lifetime; 63 | let func_name: String; 64 | let test_lt: LifetimeParam; 65 | let where_pred: WherePredicate; 66 | match variance.kind { 67 | VarianceKind::Inv(_) => return None, 68 | VarianceKind::Co(span) => { 69 | func_name = format!("covariant_wrt_{}", lt.ident); 70 | test_lt = parse_quote_spanned!(span => '__long); 71 | where_pred = parse_quote!(#test_lt: #lt); 72 | } 73 | VarianceKind::Contra(span) => { 74 | func_name = format!("contravariant_wrt_{}", lt.ident); 75 | test_lt = parse_quote_spanned!(span => '__short); 76 | where_pred = parse_quote!(#lt: #test_lt); 77 | } 78 | }; 79 | // generics for the func; original with the test lifetime inserted 80 | let mut func_generics = target_type.generics.clone(); 81 | func_generics 82 | .params 83 | .insert(0, GenericParam::Lifetime(test_lt.clone())); 84 | func_generics 85 | .make_where_clause() 86 | .predicates 87 | .push(where_pred); 88 | // generics for the funcs argument; original with 'a replaced by 'test 89 | let mut arg_generics = target_type.generics.clone(); 90 | arg_generics 91 | .lifetimes_mut() 92 | .find(|param| param.lifetime.eq(lt)) 93 | .map(|param| *param = test_lt)?; 94 | Some(CheckFn { 95 | func_name: Ident::new(&func_name, variance.span()), 96 | generics: func_generics, 97 | return_type: target_type, 98 | arg_type: TypeWithGenerics { 99 | name: &target_type.name, 100 | generics: arg_generics, 101 | }, 102 | }) 103 | } 104 | } 105 | 106 | impl<'a> ToTokens for CheckFn<'a> { 107 | fn to_tokens(&self, tokens: &mut TokenStream) { 108 | let CheckFn { 109 | func_name, 110 | arg_type, 111 | return_type, 112 | generics, 113 | } = self; 114 | let (func_generics, _, where_clause) = generics.split_for_impl(); 115 | tokens.extend(quote!( 116 | fn #func_name #func_generics(v: #arg_type) -> #return_type 117 | #where_clause 118 | { 119 | v 120 | } 121 | )); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /derive/src/variance.rs: -------------------------------------------------------------------------------- 1 | //! Types related to variance declarations 2 | use proc_macro2::{Span, TokenStream}; 3 | use quote::{quote_spanned, ToTokens}; 4 | use std::{collections::BTreeMap, fmt, mem}; 5 | use syn::punctuated::Punctuated; 6 | use syn::spanned::Spanned; 7 | use syn::{Attribute, Ident, Lifetime, Meta, Token}; 8 | 9 | use super::{Error, Result}; 10 | 11 | /// Represents the set of variances declared in the macro attributes. 12 | pub(crate) enum VarianceDeclarations { 13 | // initial state with no variances declared 14 | Empty, 15 | // state after a global declaration 16 | Global(VarianceKind), 17 | // state after 1+ specific declarations 18 | Mapping(BTreeMap), 19 | } 20 | 21 | impl VarianceDeclarations { 22 | pub(crate) fn ensure_empty(&mut self) -> Result<()> { 23 | match self { 24 | Self::Mapping(map) if !map.is_empty() => { 25 | let lifetime = map.keys().next().unwrap().clone(); 26 | Err(Error::UnexpectedLifetime(lifetime)) 27 | } 28 | _ => Ok(()), 29 | } 30 | } 31 | 32 | pub(crate) fn pop_variance<'a>( 33 | &mut self, 34 | lifetime: &'a Lifetime, 35 | options: &'a crate::TransientOptions, 36 | ) -> Variance<'a> { 37 | let kind = match self { 38 | Self::Empty => VarianceKind::default(), 39 | Self::Global(kind) => kind.clone(), 40 | Self::Mapping(map) => map.remove(&lifetime.ident).unwrap_or_default(), 41 | }; 42 | Variance { lifetime, kind, krate: &options.krate } 43 | } 44 | 45 | pub(crate) fn extract(attrs: &[Attribute]) -> Result { 46 | let mut decls = Self::Empty; 47 | for attr in attrs { 48 | decls.parse_attr_into(attr)?; 49 | } 50 | Ok(decls) 51 | } 52 | 53 | fn parse_attr_into(&mut self, attr: &Attribute) -> Result<()> { 54 | let path = attr.path(); 55 | let kind = if path.is_ident("covariant") { 56 | VarianceKind::Co(path.span()) 57 | } else if path.is_ident("contravariant") { 58 | VarianceKind::Contra(path.span()) 59 | } else { 60 | return Ok(()); 61 | }; 62 | // this attr declares a variance, so a global variance must not be set 63 | if matches!(self, Self::Global(_)) { 64 | return match mem::replace(self, Self::Empty) { 65 | Self::Global(old) => Err(Error::DuplicateVariance(old, kind)), 66 | _ => unreachable!(), 67 | }; 68 | } 69 | // this attr declares a global variance, so there must be no existing decls 70 | if let Meta::Path(_) = attr.meta { 71 | let old = match mem::replace(self, Self::Empty) { 72 | Self::Global(old) => old, 73 | Self::Mapping(map) => map.into_values().last().unwrap(), 74 | Self::Empty => { 75 | *self = Self::Global(kind); 76 | return Ok(()); 77 | } 78 | }; 79 | return Err(Error::DuplicateVariance(old, kind)); 80 | } 81 | attr.meta 82 | .require_list()? 83 | .parse_args_with(Punctuated::::parse_terminated)? 84 | .into_iter() 85 | .try_for_each(|ident| self.push(ident, kind.clone())) 86 | } 87 | 88 | fn get_mapping_mut(&mut self) -> Option<&mut BTreeMap> { 89 | match self { 90 | Self::Global(_) => None, 91 | Self::Mapping(mapping) => Some(mapping), 92 | Self::Empty => { 93 | *self = Self::Mapping(BTreeMap::new()); 94 | self.get_mapping_mut() 95 | } 96 | } 97 | } 98 | 99 | fn push(&mut self, ident: Ident, kind: VarianceKind) -> Result<()> { 100 | let map = self.get_mapping_mut().unwrap(); 101 | if let Some(old) = map.insert(ident, kind) { 102 | let new = map.values().last().unwrap().clone(); 103 | Err(Error::DuplicateVariance(old, new)) 104 | } else { 105 | Ok(()) 106 | } 107 | } 108 | } 109 | 110 | #[derive(Clone, Debug)] 111 | pub(crate) enum VarianceKind { 112 | Inv(Span), 113 | Co(Span), 114 | Contra(Span), 115 | } 116 | 117 | impl VarianceKind { 118 | pub(crate) fn span(&self) -> Span { 119 | let (Self::Inv(span) | Self::Co(span) | Self::Contra(span)) = self; 120 | *span 121 | } 122 | } 123 | 124 | impl Default for VarianceKind { 125 | fn default() -> Self { 126 | Self::Inv(Span::call_site()) 127 | } 128 | } 129 | 130 | impl fmt::Display for VarianceKind { 131 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 132 | match self { 133 | Self::Inv(_) => f.write_str("invariant"), 134 | Self::Co(_) => f.write_str("covariant"), 135 | Self::Contra(_) => f.write_str("contravariant"), 136 | } 137 | } 138 | } 139 | 140 | pub(crate) struct Variance<'a> { 141 | pub(crate) lifetime: &'a Lifetime, 142 | pub(crate) kind: VarianceKind, 143 | pub(crate) krate: &'a syn::Path, 144 | } 145 | 146 | impl<'a> ToTokens for Variance<'a> { 147 | fn to_tokens(&self, tokens: &mut TokenStream) { 148 | use VarianceKind::*; 149 | let Variance { lifetime: lt, kind, krate } = self; 150 | tokens.extend(match *kind { 151 | Inv(span) => quote_spanned!(span => #krate::Inv<#lt>), 152 | Co(span) => quote_spanned!(span => #krate::Co<#lt>), 153 | Contra(span) => quote_spanned!(span => #krate::Contra<#lt>), 154 | }); 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /src/tests.rs: -------------------------------------------------------------------------------- 1 | //! Tests for various parts of the Transient API 2 | 3 | use crate::{tr::Transient, Any, Co, Contra, Inv, TypeId}; 4 | 5 | #[cfg(feature = "alloc")] 6 | use alloc::{ 7 | boxed::Box, 8 | string::{String, ToString}, 9 | }; 10 | 11 | /// Tests for a covariant struct with no generic type parameters. 12 | #[cfg(feature = "alloc")] 13 | mod covariant { 14 | use super::*; 15 | use crate::Downcast; 16 | 17 | #[derive(Debug, Clone, PartialEq, Eq)] 18 | struct S<'a> { 19 | value: &'a String, 20 | } 21 | unsafe impl<'a> Transient for S<'a> { 22 | type Static = S<'static>; 23 | type Transience = Co<'a>; 24 | } 25 | 26 | #[test] 27 | pub(super) fn test_owned() { 28 | let value = "qwer".to_string(); 29 | let original: S<'_> = S { value: &value }; 30 | let erased: Box> + '_> = Box::new(original.clone()); 31 | 32 | // `S::Transience` is `Co<'a>` se we can erase to `Any>`, but 33 | // instead we downgraded to `Any>`. Now can't restore it b/c 34 | // the bounds require a subtype of `Co<'a>`, which `Inv<'a>` is not. 35 | // However, we need to allow the transition, but only when restoring, 36 | // not transcending. 37 | let restored: Box> = erased.downcast::>().unwrap(); 38 | assert_eq!(*restored, original); 39 | } 40 | #[test] 41 | pub(super) fn test_ref() { 42 | let value = "qwer".to_string(); 43 | let original = S { value: &value }; 44 | let erased: &dyn Any = &original; 45 | assert_eq!(erased.type_id(), TypeId::of::()); 46 | let restored = erased.downcast_ref::().unwrap(); 47 | assert_eq!(restored, &original); 48 | } 49 | #[test] 50 | pub(super) fn test_mut() { 51 | let value = "qwer".to_string(); 52 | let mut original = S { value: &value }; 53 | let erased: &mut dyn Any = &mut original; 54 | assert_eq!(erased.type_id(), TypeId::of::()); 55 | let restored = erased.downcast_mut::().unwrap().clone(); 56 | assert_eq!(restored, original); 57 | } 58 | } 59 | 60 | /// Tests for a struct with a generic type parameter 61 | #[cfg(any(feature = "std", feature = "alloc"))] 62 | mod one_type_param { 63 | use super::*; 64 | use crate::Downcast; 65 | 66 | #[derive(Debug, Clone, PartialEq, Eq)] 67 | struct S<'a, T> { 68 | value: &'a T, 69 | } 70 | unsafe impl<'a, T: 'static> Transient for S<'a, T> { 71 | type Static = S<'static, T>; 72 | type Transience = Inv<'a>; 73 | } 74 | type SS<'a> = S<'a, String>; 75 | 76 | #[test] 77 | pub(super) fn test_owned() { 78 | let value = "qwer".to_string(); 79 | let original = SS { value: &value }; 80 | let erased: Box> = Box::new(original.clone()); 81 | assert_eq!(erased.type_id(), TypeId::of::()); 82 | let restored = erased.downcast::().unwrap(); 83 | assert_eq!(*restored, original); 84 | } 85 | 86 | #[test] 87 | pub(super) fn test_ref() { 88 | let value = "qwer".to_string(); 89 | let original = SS { value: &value }; 90 | let erased: &dyn Any = &original; 91 | assert_eq!(erased.type_id(), TypeId::of::()); 92 | let restored = erased.downcast_ref::().unwrap(); 93 | assert_eq!(restored, &original); 94 | } 95 | 96 | #[test] 97 | pub(super) fn test_mut() { 98 | let value = "qwer".to_string(); 99 | let mut original = SS { value: &value }; 100 | let erased: &mut dyn Any = &mut original; 101 | assert_eq!(erased.type_id(), TypeId::of::()); 102 | let restored = erased.downcast_mut::().unwrap().clone(); 103 | assert_eq!(restored, original); 104 | } 105 | } 106 | 107 | /// Tests for a struct with a two generic type parameters 108 | mod two_type_params { 109 | use super::*; 110 | 111 | #[derive(Debug, Clone, PartialEq, Eq)] 112 | struct S<'a, T1, T2> { 113 | value1: &'a T1, 114 | value2: &'a T2, 115 | } 116 | unsafe impl<'a, T1: 'static, T2: 'static> Transient for S<'a, T1, T2> { 117 | type Static = S<'static, T1, T2>; 118 | type Transience = Inv<'a>; 119 | } 120 | } 121 | 122 | /// Tests for a struct with two lifetime params that have different variances 123 | mod mixed_lifetimes { 124 | use super::*; 125 | 126 | type ContraCo<'s, 'l> = (Contra<'s>, Co<'l>); 127 | 128 | #[derive(Debug, Clone, PartialEq, Eq)] 129 | struct M<'s, 'l> { 130 | func: fn(&'s str) -> &'static str, 131 | string: &'l str, 132 | } 133 | unsafe impl<'s, 'l> Transient for M<'s, 'l> { 134 | type Static = M<'static, 'static>; 135 | type Transience = ContraCo<'s, 'l>; 136 | } 137 | fn requires_static(_value: &dyn Any>) { 138 | /* ... */ 139 | } 140 | 141 | /// This function requires the first lifetime to lengthen from `'short` to 142 | /// `'long` (contravariance), and the second lifetime parameter to shorten 143 | /// from `'long` to `'short` (covariance). 144 | fn lengthen<'b, 'short, 'long: 'short>( 145 | short: &'b M<'short, 'long>, 146 | ) -> &'b dyn Any> { 147 | short 148 | } 149 | 150 | const STATIC_STR: &str = "static"; 151 | 152 | #[test] 153 | #[cfg(any(feature = "std", feature = "alloc"))] 154 | #[rustfmt::skip] 155 | fn test_owned() { 156 | let short: M<'_, 'static> = M { func: |_| "!", string: STATIC_STR }; 157 | let long: M<'static, '_> = M { func: |s| s, string: STATIC_STR }; 158 | let _ = lengthen(&short); 159 | 160 | let erased_short: Box + '_> = Box::new(short); 161 | assert_eq!(erased_short.type_id(), TypeId::of::()); 162 | // the first (contra) param must lengthen from `'_` to `'static` 163 | requires_static(&*erased_short); 164 | 165 | let erased_long: Box + '_> = Box::new(long); 166 | assert_eq!(erased_long.type_id(), TypeId::of::()); 167 | } 168 | 169 | #[test] 170 | #[rustfmt::skip] 171 | fn test_ref() { 172 | let short: M<'_, 'static> = M { func: |_| "!", string: STATIC_STR }; 173 | let long: M<'static, '_> = M { func: |s| s, string: STATIC_STR }; 174 | let _ = lengthen(&short); 175 | 176 | let erased_short: &dyn Any = &short; 177 | assert_eq!(erased_short.type_id(), TypeId::of::()); 178 | 179 | // the first (contra) param must lengthen from `'_` to `'static` 180 | requires_static(erased_short); 181 | 182 | let erased_long: &dyn Any = &long; 183 | assert_eq!(erased_long.type_id(), TypeId::of::()); 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | transient 2 | ===== 3 | This crate provides a reimplementation of the `std::any::Any` trait supporting 4 | types with non-`'static` lifetimes. 5 | 6 | [![Crates.io](https://img.shields.io/crates/v/transient.svg)](https://crates.io/crates/transient) 7 | 8 | ### Documentation 9 | 10 | [Module documentation with examples](https://docs.rs/transient) 11 | 12 | ### Usage 13 | 14 | To bring this crate into your repository, either add `transient` to your 15 | `Cargo.toml`, or run `cargo add transient`. 16 | 17 | Using this crate starts by implementing the provided `Transient` trait for a type, 18 | which can be derived using the included `derive` macro or implemented by hand 19 | by simply defining two associated types. Implementing this trait manually is 20 | `unsafe` but straightforward and extensively documented, and once implemented 21 | it enables this crate's functionality to be used _safely_. 22 | 23 | The following example demonstrates the trivial case of deriving the `Transient` 24 | trait for a `'static` type, and then casting it to a `dyn Any` trait object to 25 | emulate dynamic typing just as you would using the stdlib's implementation: 26 | 27 | ```rust 28 | use transient::{Transient, Any, Downcast}; 29 | 30 | #[derive(Transient, Debug, PartialEq)] 31 | struct MyUsize(usize); 32 | 33 | fn main() { 34 | let orig = MyUsize(5); 35 | // we can erase the 'static type... 36 | let erased: &dyn Any = &orig; 37 | assert!(erased.is::()); 38 | // and restore it... 39 | let restored: &MyUsize = erased.downcast_ref().unwrap(); 40 | assert_eq!(restored, &orig); 41 | // and use it in dynamically-typed shenanigans... 42 | let bananas = "bananas".to_string(); 43 | let stuff = vec![erased, &orig.0, restored, &bananas]; 44 | assert_eq!(stuff[0].downcast_ref::().unwrap(), &orig); 45 | } 46 | ``` 47 | 48 | Where it get's interesting is when you have a non-`'static` type containing 49 | borrowed data, which would be ineligable for use with the `std::any::Any` 50 | implementation due to its `'static` bound. The following example will 51 | demonstrate using the `transient` crate's implementation to utilize the 52 | same functionality as for `'static` types by simply parameterizing the 53 | `Any` trait by `Inv`, which is a `Transience` implementation that binds 54 | the lifetime and variance information that the stdlib would not be able 55 | to handle: 56 | 57 | ```rust 58 | use transient::{Transient, Any, Inv, Downcast}; 59 | 60 | #[derive(Transient, Debug, PartialEq)] 61 | struct MyUsizeRef<'a>(&'a usize); 62 | 63 | fn main() { 64 | let five = 5; 65 | let orig = MyUsizeRef(&five); 66 | // we can erase the non-'static type... 67 | let erased: &dyn Any = &orig; 68 | assert!(erased.is::()); 69 | // and restore it... 70 | let restored: &MyUsizeRef = erased.downcast_ref().unwrap(); 71 | assert_eq!(restored, &orig); 72 | // and use it in dynamically-typed shenanigans... 73 | let stuff = vec![erased, &five, restored, &"bananas"]; 74 | assert_eq!(stuff[0].downcast_ref::().unwrap(), &orig); 75 | } 76 | ``` 77 | 78 | The `Inv` type used above stands for "invariant", which is the most conservative 79 | form of a property known as [variance] that describes the behavior of a type 80 | with respect to a lifetime parameter. An understanding of variance will let 81 | you utilize the advanced features of this crate, but is not necessary for most 82 | purposes since the `Inv` type shown above be safely used for _any_ type with 83 | a single lifetime parameter. 84 | 85 | In the first example where we cast our type to a naked `dyn Any` without specifying 86 | a `Transience` type, the `Any` trait's default type parameter `()` was chosen 87 | implicitly which causes it to behave like the stdlib's implementation by only 88 | accepting `'static` types; trying this with `MyUsizeRef` defined in the second 89 | example would have been rejected by the compiler. This hints at the underlying 90 | mechanism used to implement this crate, wherein types declare their temporal 91 | relationships (i.e. `Transience`) when implementing the `Transient` trait, which 92 | then bounds the range of `dyn Any<_>` flavors they are allowed to utilize. 93 | Non-`'static` types with a single lifetime `'a` that implement `Transient` using 94 | the derive macro are (by default) assigned a `Transience` of `Inv<'a>`, which 95 | limits them to being erased to (and restored from) the `dyn Any>` trait 96 | object. By contrast, `'static` types implement the most flexible `Transience` 97 | of `()` which allows them to be be cast to any `dyn Any<_>` they want, up to 98 | and including the default `dyn Any()`. 99 | 100 | There is a large amount of middle-ground between these two extremes which is 101 | discussed in-depth throughout the documentation (hint - there are `Co` and 102 | `Contra` types as well), but the key takeaway is that types make a single 103 | `unsafe` but straight-forward decision about their temporal behavior when 104 | implementing the `Transient` trait, and then everything else is kept _safe_ 105 | using the type system and trait bounds. 106 | 107 | ### Usage: multiple life parameters 108 | 109 | The mechanism demonstrated above extends naturally to types with more than one 110 | lifetime parameter by instead parameterizing the `dyn Any<_>` with a tuple as 111 | shown in the following example; however, the included `derive` macro currently 112 | only supports types with zero or one lifetime parameters, so we will implement 113 | the `Transient` trait ourselves this time: 114 | 115 | ```rust 116 | use transient::{Transient, Any, Inv, Downcast}; 117 | 118 | #[derive(Debug, PartialEq)] 119 | struct TwoRefs<'a, 'b>(&'a i32, &'b i32); 120 | 121 | unsafe impl<'a, 'b> Transient for TwoRefs<'a, 'b> { 122 | // the `Static` type is simply the same as the `Self` type with its 123 | // lifetimes replaced by `'static` 124 | type Static = TwoRefs<'static, 'static>; 125 | // we use a tuple for the `Transience` that covers both lifetimes, using 126 | // `Inv` for each element since this is always safe 127 | type Transience = (Inv<'a>, Inv<'b>); 128 | } 129 | 130 | fn main() { 131 | let (five, seven) = (5, 7); 132 | let orig = TwoRefs(&five, &seven); 133 | let erased: &dyn Any = &orig; 134 | assert!(erased.is::()); 135 | let restored: &TwoRefs = erased.downcast_ref().unwrap(); 136 | assert_eq!(restored, &orig); 137 | let stuff = vec![erased, &five, restored, &"bananas"]; 138 | assert_eq!(stuff[0].downcast_ref::().unwrap(), &orig); 139 | } 140 | ``` 141 | 142 | ### License 143 | 144 | This project is licensed under either of 145 | 146 | * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or 147 | https://www.apache.org/licenses/LICENSE-2.0) 148 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or 149 | https://opensource.org/licenses/MIT) 150 | 151 | at your option. 152 | 153 | [variance]: https://doc.rust-lang.org/nomicon/subtyping.html 154 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2024-present Joshua Rudolph. https://github.com/JRRudy1 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | 15 | 16 | Apache License 17 | Version 2.0, January 2004 18 | http://www.apache.org/licenses/ 19 | 20 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 21 | 22 | 1. Definitions. 23 | 24 | "License" shall mean the terms and conditions for use, reproduction, 25 | and distribution as defined by Sections 1 through 9 of this document. 26 | 27 | "Licensor" shall mean the copyright owner or entity authorized by 28 | the copyright owner that is granting the License. 29 | 30 | "Legal Entity" shall mean the union of the acting entity and all 31 | other entities that control, are controlled by, or are under common 32 | control with that entity. For the purposes of this definition, 33 | "control" means (i) the power, direct or indirect, to cause the 34 | direction or management of such entity, whether by contract or 35 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 36 | outstanding shares, or (iii) beneficial ownership of such entity. 37 | 38 | "You" (or "Your") shall mean an individual or Legal Entity 39 | exercising permissions granted by this License. 40 | 41 | "Source" form shall mean the preferred form for making modifications, 42 | including but not limited to software source code, documentation 43 | source, and configuration files. 44 | 45 | "Object" form shall mean any form resulting from mechanical 46 | transformation or translation of a Source form, including but 47 | not limited to compiled object code, generated documentation, 48 | and conversions to other media types. 49 | 50 | "Work" shall mean the work of authorship, whether in Source or 51 | Object form, made available under the License, as indicated by a 52 | copyright notice that is included in or attached to the work 53 | (an example is provided in the Appendix below). 54 | 55 | "Derivative Works" shall mean any work, whether in Source or Object 56 | form, that is based on (or derived from) the Work and for which the 57 | editorial revisions, annotations, elaborations, or other modifications 58 | represent, as a whole, an original work of authorship. For the purposes 59 | of this License, Derivative Works shall not include works that remain 60 | separable from, or merely link (or bind by name) to the interfaces of, 61 | the Work and Derivative Works thereof. 62 | 63 | "Contribution" shall mean any work of authorship, including 64 | the original version of the Work and any modifications or additions 65 | to that Work or Derivative Works thereof, that is intentionally 66 | submitted to Licensor for inclusion in the Work by the copyright owner 67 | or by an individual or Legal Entity authorized to submit on behalf of 68 | the copyright owner. For the purposes of this definition, "submitted" 69 | means any form of electronic, verbal, or written communication sent 70 | to the Licensor or its representatives, including but not limited to 71 | communication on electronic mailing lists, source code control systems, 72 | and issue tracking systems that are managed by, or on behalf of, the 73 | Licensor for the purpose of discussing and improving the Work, but 74 | excluding communication that is conspicuously marked or otherwise 75 | designated in writing by the copyright owner as "Not a Contribution." 76 | 77 | "Contributor" shall mean Licensor and any individual or Legal Entity 78 | on behalf of whom a Contribution has been received by Licensor and 79 | subsequently incorporated within the Work. 80 | 81 | 2. Grant of Copyright License. Subject to the terms and conditions of 82 | this License, each Contributor hereby grants to You a perpetual, 83 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 84 | copyright license to reproduce, prepare Derivative Works of, 85 | publicly display, publicly perform, sublicense, and distribute the 86 | Work and such Derivative Works in Source or Object form. 87 | 88 | 3. Grant of Patent License. Subject to the terms and conditions of 89 | this License, each Contributor hereby grants to You a perpetual, 90 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 91 | (except as stated in this section) patent license to make, have made, 92 | use, offer to sell, sell, import, and otherwise transfer the Work, 93 | where such license applies only to those patent claims licensable 94 | by such Contributor that are necessarily infringed by their 95 | Contribution(s) alone or by combination of their Contribution(s) 96 | with the Work to which such Contribution(s) was submitted. If You 97 | institute patent litigation against any entity (including a 98 | cross-claim or counterclaim in a lawsuit) alleging that the Work 99 | or a Contribution incorporated within the Work constitutes direct 100 | or contributory patent infringement, then any patent licenses 101 | granted to You under this License for that Work shall terminate 102 | as of the date such litigation is filed. 103 | 104 | 4. Redistribution. You may reproduce and distribute copies of the 105 | Work or Derivative Works thereof in any medium, with or without 106 | modifications, and in Source or Object form, provided that You 107 | meet the following conditions: 108 | 109 | (a) You must give any other recipients of the Work or 110 | Derivative Works a copy of this License; and 111 | 112 | (b) You must cause any modified files to carry prominent notices 113 | stating that You changed the files; and 114 | 115 | (c) You must retain, in the Source form of any Derivative Works 116 | that You distribute, all copyright, patent, trademark, and 117 | attribution notices from the Source form of the Work, 118 | excluding those notices that do not pertain to any part of 119 | the Derivative Works; and 120 | 121 | (d) If the Work includes a "NOTICE" text file as part of its 122 | distribution, then any Derivative Works that You distribute must 123 | include a readable copy of the attribution notices contained 124 | within such NOTICE file, excluding those notices that do not 125 | pertain to any part of the Derivative Works, in at least one 126 | of the following places: within a NOTICE text file distributed 127 | as part of the Derivative Works; within the Source form or 128 | documentation, if provided along with the Derivative Works; or, 129 | within a display generated by the Derivative Works, if and 130 | wherever such third-party notices normally appear. The contents 131 | of the NOTICE file are for informational purposes only and 132 | do not modify the License. You may add Your own attribution 133 | notices within Derivative Works that You distribute, alongside 134 | or as an addendum to the NOTICE text from the Work, provided 135 | that such additional attribution notices cannot be construed 136 | as modifying the License. 137 | 138 | You may add Your own copyright statement to Your modifications and 139 | may provide additional or different license terms and conditions 140 | for use, reproduction, or distribution of Your modifications, or 141 | for any such Derivative Works as a whole, provided Your use, 142 | reproduction, and distribution of the Work otherwise complies with 143 | the conditions stated in this License. 144 | 145 | 5. Submission of Contributions. Unless You explicitly state otherwise, 146 | any Contribution intentionally submitted for inclusion in the Work 147 | by You to the Licensor shall be under the terms and conditions of 148 | this License, without any additional terms or conditions. 149 | Notwithstanding the above, nothing herein shall supersede or modify 150 | the terms of any separate license agreement you may have executed 151 | with Licensor regarding such Contributions. 152 | 153 | 6. Trademarks. This License does not grant permission to use the trade 154 | names, trademarks, service marks, or product names of the Licensor, 155 | except as required for reasonable and customary use in describing the 156 | origin of the Work and reproducing the content of the NOTICE file. 157 | 158 | 7. Disclaimer of Warranty. Unless required by applicable law or 159 | agreed to in writing, Licensor provides the Work (and each 160 | Contributor provides its Contributions) on an "AS IS" BASIS, 161 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 162 | implied, including, without limitation, any warranties or conditions 163 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 164 | PARTICULAR PURPOSE. You are solely responsible for determining the 165 | appropriateness of using or redistributing the Work and assume any 166 | risks associated with Your exercise of permissions under this License. 167 | 168 | 8. Limitation of Liability. In no event and under no legal theory, 169 | whether in tort (including negligence), contract, or otherwise, 170 | unless required by applicable law (such as deliberate and grossly 171 | negligent acts) or agreed to in writing, shall any Contributor be 172 | liable to You for damages, including any direct, indirect, special, 173 | incidental, or consequential damages of any character arising as a 174 | result of this License or out of the use or inability to use the 175 | Work (including but not limited to damages for loss of goodwill, 176 | work stoppage, computer failure or malfunction, or any and all 177 | other commercial damages or losses), even if such Contributor 178 | has been advised of the possibility of such damages. 179 | 180 | 9. Accepting Warranty or Additional Liability. While redistributing 181 | the Work or Derivative Works thereof, You may choose to offer, 182 | and charge a fee for, acceptance of support, warranty, indemnity, 183 | or other liability obligations and/or rights consistent with this 184 | License. However, in accepting such obligations, You may act only 185 | on Your own behalf and on Your sole responsibility, not on behalf 186 | of any other Contributor, and only if You agree to indemnify, 187 | defend, and hold each Contributor harmless for any liability 188 | incurred by, or claims asserted against, such Contributor by reason 189 | of your accepting any such warranty or additional liability. -------------------------------------------------------------------------------- /derive/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Defines the [`Transient`][crate::Transient] derive macro that safely implements 2 | //! the [`Transient`][transient::tr::Transient] trait for any struct with at most 4 3 | //! lifetime parameters. 4 | #![allow(unknown_lints, clippy::needless_lifetimes)] 5 | use proc_macro::TokenStream; 6 | use proc_macro2::{Span, TokenStream as TokenStream2}; 7 | use quote::{quote, ToTokens}; 8 | use syn::spanned::Spanned; 9 | use syn::{parse_macro_input, parse_quote_spanned}; 10 | use syn::{Data, DeriveInput, Generics, Ident, Lifetime}; 11 | 12 | mod checks; 13 | mod options; 14 | mod utils; 15 | mod variance; 16 | 17 | use checks::ChecksModule; 18 | use options::TransientOptions; 19 | use utils::{extract_lifetimes, insert_static_predicates, TypeWithGenerics}; 20 | use variance::{Variance, VarianceDeclarations, VarianceKind}; 21 | 22 | // Maximum tuple length for which `Transience` is implemented 23 | const MAX_LIFETIMES: usize = 5; 24 | 25 | /// Derive macro that implements the [`Transient`] trait for any struct. 26 | /// 27 | /// Note that when this macro is applied to a `'static` struct with no lifetime 28 | /// parameters, it will instead implement the [`Static`] trait which results in 29 | /// a blanket impl of the `Transient` trait. 30 | /// 31 | /// This macro is limited to structs satisfying the following conditions: 32 | /// - There must be at most 5 lifetime parameters. This is not a limitation of 33 | /// the derive macro, but in the tuple length for which the `Transience` trait 34 | /// is implemented. 35 | /// - There may be any number of type (or const) parameters, but the trait will 36 | /// only be implemented where `T: 'static` for each type parameter `T`. 37 | /// 38 | /// # Customization 39 | /// 40 | /// ## Variance 41 | /// By default, the [variance] of a deriving struct is assumed to be _invariant_ 42 | /// respect to its lifetime parameter (if it has one), since this is the only 43 | /// type of variance that can be safely used for _all_ types without analyzing 44 | /// the behavior of its fields (which this macro does not attempt to do). When 45 | /// the added flexibility of _covariance_ or _contravariance_ is needed, the 46 | /// `covariant` and `contravariant` helper attributes can be used to override 47 | /// this default. When these attributes are used, a test module will be generated 48 | /// which defines a function for each lifetime that will fail to compile if the 49 | /// requested variance is not appropriate for the type. 50 | /// 51 | /// One or more of these attributes may declared in the following forms: 52 | /// 1. `#[covariant]` or `#[contravariant]` 53 | /// 2. `#[covariant()]` or `#[contravariant()]` 54 | /// 3. `#[covariant(a)]` or `#[contravariant(a)]` 55 | /// 4. `#[covariant(a, b, c)]` or `#[contravariant(a, b, c)]` 56 | /// 57 | /// Option 1 is a global declaration of the variance for ALL lifetimes, and must be 58 | /// the only declaration. Option 2 is a no-op, but still conflicts with the first 59 | /// option. Option 3 declares the variance for the type w.r.t. `'a`, and can coexist 60 | /// with other (non-global) declarations as long as there is no overlap. Option 4 61 | /// declares the variance w.r.t. each of the listed lifetimes, subject to the same 62 | /// rules as for option 3. 63 | /// 64 | /// ## Crate path 65 | /// If it is desired that the `transient` crate's symbols be referenced from an 66 | /// alternate path in the generated impl, such as a re-export of the symbols in a 67 | /// downstream library, the `#[transient(crate = path::to::transient)]` attribute 68 | /// can be applied to the struct. By default, the path will be `::transient`. 69 | /// 70 | /// # Failure modes 71 | /// This macro can fail for any of the following reasons: 72 | /// - The declared variance is not valid for the type. The compiler error generated 73 | /// in this case is not particularly helpful, but can be recognized by suggestions 74 | /// such as "help: consider adding the following bound: `'a: '__long`". 75 | /// - Requesting any variance for a type with no lifetime parameters 76 | /// - Requesting a variance for a lifetime that does not appear on the struct 77 | /// - Declaring a "global" variance in addition to a lifetime-specific variance 78 | /// - Providing more than one "variance" attribute for a lifetime parameter 79 | /// 80 | /// # Example - struct with a type parameter and a lifetime parameter 81 | /// 82 | /// Invocation: 83 | /// ``` 84 | /// use transient::Transient; 85 | /// 86 | /// #[derive(Debug, Clone, PartialEq, Eq, Transient)] 87 | /// struct S<'a, T> { 88 | /// value: &'a T 89 | /// } 90 | /// ``` 91 | /// 92 | /// Generated impl: 93 | /// ``` 94 | /// # struct S<'a, T> {value: &'a T} 95 | /// unsafe impl<'a, T> ::transient::Transient for S<'a, T> 96 | /// where 97 | /// T: 'static, 98 | /// { 99 | /// type Static = S<'static, T>; 100 | /// type Transience = transient::Inv<'a>; 101 | /// } 102 | /// ``` 103 | /// 104 | /// # Example - struct with two lifetime parameters and a _covariance_ declaration 105 | /// 106 | /// Invocation: 107 | /// ``` 108 | /// use transient::Transient; 109 | /// 110 | /// #[derive(Debug, Clone, PartialEq, Eq, Transient)] 111 | /// #[covariant(a)] 112 | /// struct S<'a, 'b> { 113 | /// name: &'a String, // declared covariant 114 | /// values: &'b [i32], // no variance specified, defaults to invariant 115 | /// } 116 | /// # fn main() {} 117 | /// ``` 118 | /// 119 | /// Generated impl: 120 | /// ``` 121 | /// # struct S<'a, 'b> {name: &'a String, values: &'b [i32]} 122 | /// unsafe impl<'a, 'b> ::transient::Transient for S<'a, 'b> { 123 | /// type Static = S<'static, 'static>; 124 | /// type Transience = (transient::Co<'a>, transient::Inv<'a>); 125 | /// } 126 | /// ``` 127 | /// 128 | /// This invocation also generates a test module, not shown above, including a 129 | /// function that will fail to compile if the declared variance is not correct 130 | /// for the type. 131 | /// 132 | /// # Example - struct with a type parameter and `where` clause but no lifetimes 133 | /// 134 | /// Invocation: 135 | /// ``` 136 | /// use transient::Transient; 137 | /// 138 | /// #[derive(Debug, Clone, PartialEq, Eq, Transient)] 139 | /// struct S where T: Clone { 140 | /// value: T 141 | /// } 142 | /// ``` 143 | /// 144 | /// Generated impl: 145 | /// ``` 146 | /// # struct S where T: Clone {value: T} 147 | /// impl transient::Static for S 148 | /// where 149 | /// T: Clone + 'static, 150 | /// {} 151 | /// ``` 152 | /// 153 | /// [`Transient`]: ../transient/trait.Transient.html 154 | /// [`Static`]: ../transient/trait.Static.html 155 | /// [variance]: https://doc.rust-lang.org/nomicon/subtyping.html 156 | #[proc_macro_derive(Transient, attributes(transient, covariant, contravariant))] 157 | pub fn derive_transient(input: TokenStream) -> TokenStream { 158 | let input = parse_macro_input!(input as DeriveInput); 159 | match generate_impls(input) { 160 | Ok(tokens) => tokens.into(), 161 | Err(err) => err.into_compile_error().into(), 162 | } 163 | } 164 | 165 | fn generate_impls(input: DeriveInput) -> Result { 166 | if !matches!(input.data, Data::Struct(_)) { 167 | return Err(Error::NotAStruct(input.ident.span())); 168 | } 169 | let options = TransientOptions::extract(&input.attrs)?; 170 | let mut variance_decls = VarianceDeclarations::extract(&input.attrs)?; 171 | let self_type = SelfType::new(input.ident, input.generics)?; 172 | if self_type.is_static() { 173 | variance_decls.ensure_empty()?; 174 | Ok(StaticImpl(&self_type, &options).into_token_stream()) 175 | } else { 176 | let static_type = self_type.get_static_type(); 177 | let transience = self_type.resolve_transience(variance_decls, &options)?; 178 | let transient_impl = TransientImpl(&self_type, static_type, transience, &options); 179 | let checks_module = ChecksModule::new(&transient_impl); 180 | Ok(quote!(#transient_impl #checks_module)) 181 | } 182 | } 183 | 184 | /// Represents the `impl Static` block for a 'static type 185 | struct StaticImpl<'a>(&'a SelfType, &'a TransientOptions); 186 | 187 | impl<'a> ToTokens for StaticImpl<'a> { 188 | fn to_tokens(&self, tokens: &mut TokenStream2) { 189 | let StaticImpl(self_type, TransientOptions { krate }) = self; 190 | let (impl_generics, _, where_clause) = self_type.generics.split_for_impl(); 191 | tokens.extend(quote!( 192 | impl #impl_generics #krate::Static for #self_type #where_clause {} 193 | )); 194 | } 195 | } 196 | 197 | /// Represents the `impl Transient` block for a non-'static type 198 | struct TransientImpl<'a>( 199 | &'a SelfType, 200 | StaticType<'a>, 201 | Transience<'a>, 202 | &'a TransientOptions, 203 | ); 204 | 205 | impl<'a> ToTokens for TransientImpl<'a> { 206 | fn to_tokens(&self, tokens: &mut TokenStream2) { 207 | let TransientImpl(self_type, static_type, transience, options) = self; 208 | let krate = &options.krate; 209 | let (impl_generics, _, where_clause) = self_type.generics.split_for_impl(); 210 | tokens.extend(quote!( 211 | unsafe impl #impl_generics #krate::Transient for #self_type #where_clause { 212 | type Static = #static_type; 213 | type Transience = #transience; 214 | } 215 | )); 216 | } 217 | } 218 | 219 | /// The target type implementing the `Static` or `Transient` trait 220 | struct SelfType { 221 | name: Ident, 222 | generics: Generics, 223 | lifetimes: Vec, 224 | } 225 | 226 | impl SelfType { 227 | fn new(name: Ident, mut generics: Generics) -> Result { 228 | insert_static_predicates(&mut generics); 229 | let lifetimes = extract_lifetimes(&generics); 230 | if lifetimes.len() > MAX_LIFETIMES { 231 | let extra = lifetimes.into_iter().nth(MAX_LIFETIMES).unwrap(); 232 | return Err(Error::TooManyLifetimes(extra.span())); 233 | } 234 | Ok(SelfType { name, generics, lifetimes }) 235 | } 236 | /// Query whether the type is 'static 237 | fn is_static(&self) -> bool { 238 | self.lifetimes.is_empty() 239 | } 240 | /// Get the `Static` associated type by replacing lifetimes with 'static 241 | fn get_static_type(&self) -> StaticType<'_> { 242 | let mut generics = self.generics.clone(); 243 | generics 244 | .lifetimes_mut() 245 | .for_each(|lt| *lt = parse_quote_spanned!(lt.span() => 'static)); 246 | StaticType { name: &self.name, generics } 247 | } 248 | /// Attempt to unify the lifetimes of the type and the variances declared in its 249 | /// attributes to establish the variance with respect to each lifetime. 250 | fn resolve_transience<'a>( 251 | &'a self, 252 | mut decls: VarianceDeclarations, 253 | options: &'a TransientOptions, 254 | ) -> Result> { 255 | // pop a variance from the declarations for each lifetime or use the default 256 | let variances = self 257 | .lifetimes 258 | .iter() 259 | .map(|lt| decls.pop_variance(lt, options)) 260 | .collect::>(); 261 | // check for remaining declarations that correspond to invalid lifetimes 262 | decls.ensure_empty()?; 263 | Ok(Transience(variances)) 264 | } 265 | } 266 | 267 | impl ToTokens for SelfType { 268 | fn to_tokens(&self, tokens: &mut TokenStream2) { 269 | let Self { name, generics, .. } = self; 270 | let type_generics = generics.split_for_impl().1; 271 | quote!(#name #type_generics).to_tokens(tokens); 272 | } 273 | } 274 | 275 | /// The `Transient::Static` associated type 276 | type StaticType<'a> = TypeWithGenerics<'a>; 277 | 278 | /// The `Transient::Transience` associated type 279 | struct Transience<'a>(Vec>); 280 | 281 | impl<'a> ToTokens for Transience<'a> { 282 | fn to_tokens(&self, tokens: &mut TokenStream2) { 283 | let variances = &self.0; 284 | if variances.len() == 1 { 285 | variances[0].to_tokens(tokens); 286 | } else { 287 | quote!((#(#variances,)*)).to_tokens(tokens); 288 | }; 289 | } 290 | } 291 | 292 | /// Collection of internal error conditions 293 | #[derive(Debug, thiserror::Error)] 294 | pub(crate) enum Error { 295 | #[error(transparent)] 296 | Syn(#[from] syn::Error), 297 | #[error("Only `struct`'s are supported!")] 298 | NotAStruct(Span), 299 | #[error("At most {} lifetime parameters are allowed!", MAX_LIFETIMES)] 300 | TooManyLifetimes(Span), 301 | #[error("Duplicate variance specification! '{0}' replaced by '{1}'")] 302 | DuplicateVariance(VarianceKind, VarianceKind), 303 | #[error("Variance declared for an invalid lifetime `'{0}`!")] 304 | UnexpectedLifetime(Ident), 305 | #[error("Invalid option!")] 306 | InvalidOption(Span), 307 | } 308 | 309 | pub(crate) type Result = std::result::Result; 310 | 311 | impl Error { 312 | fn into_compile_error(self) -> TokenStream2 { 313 | syn::Error::from(self).into_compile_error() 314 | } 315 | fn span(&self) -> Span { 316 | match self { 317 | Self::Syn(err) => err.span(), 318 | Self::DuplicateVariance(_, new) => new.span(), 319 | Self::UnexpectedLifetime(lifetime) => lifetime.span(), 320 | Self::NotAStruct(span) | Self::TooManyLifetimes(span) | Self::InvalidOption(span) => { 321 | *span 322 | } 323 | } 324 | } 325 | } 326 | 327 | impl From for syn::Error { 328 | fn from(value: Error) -> Self { 329 | match value { 330 | Error::Syn(err) => err, 331 | err => Self::new(err.span(), err.to_string()), 332 | } 333 | } 334 | } 335 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! This crate provides the [`transient::Any`] trait which re-implements the 2 | //! dynamic typing mechanism provided by [`std::any::Any`] to add support for 3 | //! types with non-`'static` lifetimes. 4 | //! 5 | //! # Introduction 6 | //! The standard library's [`Any`] trait is used to emulate dynamic typing within 7 | //! Rust, and is extremely useful in cases where implementing a statically typed 8 | //! solution would be inconvenient, if not impossible. Examples include storing 9 | //! heterogeneous values in a `Vec`, or eliminating generic parameters from a 10 | //! type so that it can be used in object-safe trait methods. 11 | //! 12 | //! However, a significant limitation of the `std::any::Any` trait is its `'static` 13 | //! lifetime bound, which prevents it from being used for types containing any 14 | //! non-`'static` references. This restriction eliminates many potential use-cases, 15 | //! and in others it can force users to sacrifice performance by cloning data that 16 | //! could otherwise be borrowed. 17 | //! 18 | //! The crate provides a re-implemented [`Any`] trait that circumvents this limitation 19 | //! to allow type-erasure to be applied to *transient* (i.e. non-`'static`) types. 20 | //! This is achieved by modeling a Rust type as decomposable into separate components 21 | //! for its _raw static data_ and its _lifetime parameters_, as embodied by the 22 | //! `Static` and `Transience` associated types of the provided [`Transient`] trait. 23 | //! In this implementation, the `Static` component is used to obtain the unique 24 | //! [`TypeId`] of the type (which the compiler only hands out for `T: 'static`), 25 | //! and the `Transience` is used as a generic parameter on the re-implemented 26 | //! [`Any`] trait to bound the allowable transitions and uphold Rust's strict 27 | //! safety guarantees. 28 | //! 29 | //! # Features 30 | //! - Near drop-in replacement for `std::any::Any` when dealing with `'static` types 31 | //! - Familiar extension beyond `std::any::Any` when dealing with non-`'static` types 32 | //! - Zero run-time cost above that of a standard `dyn Any` cast, with all added 33 | //! functionality implemented using the type system 34 | //! - Safely accounts for the nuances of _subtyping and variance_ 35 | //! - Supports types with any number of generic lifetime parameters with arbitrary 36 | //! variance combinations 37 | //! - Supports types with any number of generic type parameters 38 | //! - Provides the [`macro@Transient`] `derive` macro to implement the `Transient` 39 | //! trait for most types 40 | //! - Supports `no_std` environments with or without `alloc` 41 | //! 42 | //! # Limitations 43 | //! - Requires a single `unsafe` trait to be implemented for types wishing to 44 | //! utilize the crate's functionality; however, this trait is usually trivial 45 | //! to safely implement, and a `derive` macro is provided for common cases 46 | //! - Only `Sized` types are supported. Removing this restriction would be 47 | //! trivial, but makes it awkward to name generic types that require their 48 | //! parameters to be `T: Sized` since `T::Static: Sized` must be explicitly 49 | //! stated even when `T: Sized` can be implied 50 | //! 51 | //! # Examples 52 | //! 53 | //! The first step in using this crate is to implement the [`Transient`] trait 54 | //! for a type. Implementations of this trait are provided for many stdlib 55 | //! types, it can be derived for most custom types, and it is easy to implement 56 | //! by hand when more flexibility is needed. Implementations for common types 57 | //! provided by some 3rd party libraries are also available behind eponymous 58 | //! feature flags (currently only `ndarray`, `pyo3`, and `numpy`, but feel free 59 | //! to submit an issue/PR requesting others). 60 | //! 61 | //! In the trivial case of a `'static` type with no lifetime parameters, the 62 | //! `transient` crate's [`Any`] trait can be used just like that of the standard 63 | //! library once the `Transient` trait has been implemented or derived: 64 | //! ``` 65 | //! # fn main() { 66 | //! # #[cfg(feature = "derive")] { 67 | //! use transient::*; 68 | //! 69 | //! #[derive(Transient, Debug, PartialEq)] 70 | //! struct Usize(usize); 71 | //! 72 | //! let orig = Usize(5); 73 | //! 74 | //! let erased: &dyn Any = &orig; 75 | //! assert_eq!(TypeId::of::(), erased.type_id()); 76 | //! 77 | //! let restored: &Usize = erased.downcast_ref::().unwrap(); 78 | //! assert_eq!(restored, &orig); 79 | //! # }} 80 | //! ``` 81 | //! The trick is that the `Any` trait as used above is actually generic over a 82 | //! type known as the `Transience`, which defaults to `()`; so the relevant line 83 | //! in the above snippet actually desugars to `erased: &'_ dyn Any<()> = &orig`. 84 | //! This form of the `Any` trait only supports `'static` types, just like the 85 | //! stdlib implementation. 86 | //! 87 | //! Where it gets interesting is when a type is *not* `'static`, for which the 88 | //! `Any` trait can be parameterized by a [`Transience`] type. In the case of 89 | //! a type with a single lifetime parameter, this can simply be one of three types 90 | //! provided by this crate, [`Inv`], [`Co`], and [`Contra`], which represent the 91 | //! three flavors of [variance] a type can have with respect to a lifetime parameter. 92 | //! While choosing the correct variance would typically be a safety-critical 93 | //! decision, the valid choices for the variance of a type are bounded by its 94 | //! implementation of the `Transient` trait, and the compiler will prevent you 95 | //! from using a transience that would not be sound. 96 | //! 97 | //! We will return to the topic of `Transience` in a bit, but for now lets choose 98 | //! `Inv` (*invariant*) which is the most conservative form of variance that all 99 | //! (single-lifetime) types can use. To do this, simply replace `dyn Any` with 100 | //! `dyn Any` when coercing a `Box` or reference to the trait object: 101 | //! ``` 102 | //! # fn main() { 103 | //! # #[cfg(feature = "derive")] { 104 | //! use transient::*; 105 | //! 106 | //! #[derive(Transient, Debug, PartialEq)] 107 | //! struct UsizeRef<'a>(&'a usize); 108 | //! 109 | //! let five = 5; 110 | //! let orig = UsizeRef(&five); 111 | //! 112 | //! let erased: &dyn Any = &orig; 113 | //! assert!(erased.is::()); 114 | //! assert_eq!(TypeId::of::(), erased.type_id()); 115 | //! 116 | //! let restored: &UsizeRef = erased.downcast_ref().unwrap(); 117 | //! assert_eq!(restored, &orig); 118 | //! # }} 119 | //! ``` 120 | //! 121 | //! And that's all it takes! Things get a slightly spicier in more complicated 122 | //! scenarios, but this crate aims to make the process as painless and intuitive 123 | //! as possible while safely providing a high degree of flexibility for all the 124 | //! niche cases you can imagine. 125 | //! 126 | //! 127 | //! # Overview 128 | //! 129 | //! ## The `Any` trait 130 | //! The most important item provided by this crate is the [`Any`] trait, which is 131 | //! modeled after the standard library's [`std::any::Any`] trait. Much like the 132 | //! stdlib version, this trait typically appears as the opaque `dyn Any` trait 133 | //! object that can be _downcast_ back into an original concrete type. The key 134 | //! difference is that, while the `std::any::Any` trait is implemented for all 135 | //! `T: 'static`, the [`transient::Any`] trait is instead implemented for all 136 | //! `T: Transient` (as discussed in the next section). The `transient::Any` 137 | //! trait is also different in that it has a generic type parameter know as 138 | //! he `Transience` (discussed in another upcoming section) which is used to 139 | //! enable the support for non-`'static` types that forms the motivation for 140 | //! this crate. 141 | //! 142 | //! ## The `Transient` Trait 143 | //! The [`Transient`] trait is an extremely simple, but `unsafe` trait consisting 144 | //! only of two associated types: 145 | //! ```skip 146 | //! pub unsafe trait Transient { 147 | //! type Static: 'static; 148 | //! type Transience: Transience; 149 | //! /* provided methods hidden */ 150 | //! } 151 | //! ``` 152 | //! The first associated type `Static` is referred to as the *static type* of the 153 | //! implementing type, and is simply the same type but with its lifetime parameters 154 | //! replaced by `'static` (e.g., a struct `S<'a, 'b>` would define `Static` as 155 | //! `S<'static, 'static>`). The static type is used to obtain a [`TypeId`] that 156 | //! uniquely identifies the (`'static` version of the) erased type so that it can 157 | //! be safely downcast from an opaque trait object to the concrete type. However, 158 | //! the compiler only assigns `TypeId`s for `'static` types, so any information 159 | //! about the true lifetime parameters of the `Transient` type is lost. Another 160 | //! mechanism is therefore needed to restore this lifetime information so that 161 | //! the borrow checker can continue to maintain Rust's safety guarantees. 162 | //! 163 | //! The second associated type `Transience` provides this mechanism by capturing 164 | //! the lifetime (and *[variance]*) information that the static type is missing. 165 | //! To accomplish this, the `transient` crate provides the `Co`, `Contra` and `Inv` 166 | //! structs that exhibit the 3 forms of variance for a single lifetime parameter, 167 | //! which can be then combined in tuples to accommodate types with multiple (or 168 | //! zero) lifetime parameters. This type plays several key roles in the safety 169 | //! and flexibility of this crate's functionality, as will be discussed below. 170 | //! 171 | //! Implementing this trait for a type, either manually or by using the included 172 | //! [derive macro][macro@Transient], is the key ingredient to utilizing the 173 | //! functionality of this crate and is discussed in-depth in 174 | //! [its documentation][Transient]. 175 | //! 176 | //! ## The `Transience` trait 177 | //! In common language, **transience** is a noun that can be defined as 178 | //! [*the quality or state of being transient*]. The `transient` crate adopts this 179 | //! term throughout its code and documentation to describe the relationship that a 180 | //! data structure has with the 0 or more lifetimes parameters it depends on, as 181 | //! codified by the [`Transience`] trait. More specifically, this therm refers the 182 | //! [variance] of a type with respect to each of its generic lifetime parameters, 183 | //! which is a fairly niche topic in everyday Rust programming by plays a major 184 | //! role in the implementation of this crate. 185 | //! 186 | //! ### Transience bounds and transitions 187 | //! A simplified version of this crate's functionality could be implemented by 188 | //! simply allowing a type `T: Transient` to be cast to, and restored from, a 189 | //! `dyn Any` trait object. This would be sufficient in some cases, 190 | //! but is has a significant limitation in that two types `S` and `T` with 191 | //! differing `Transience` types would erase to different trait objects; the 192 | //! erased types `dyn Any` and `dyn Any` would 193 | //! be distinct types that could not be used interchangeably or stored together 194 | //! in a homogeneous container. 195 | //! 196 | //! To evade this limitation and provide maximum flexibility, the `transient::Any` 197 | //! trait has a bounded blanket implementation that allows a type to erase to 198 | //! any transience which is more (or equally) conservative than its own. For 199 | //! example, a type `struct S<'long>(&'long i32)` that implements `Transient` 200 | //! with a `Transience` of `Co<'long>` can be erased to `dyn Any<_>` with any 201 | //! compatible transience such as `Co<'long>`, `Co<'short>`, `Inv<'long>`, and 202 | //! `Inv<'short>`. 203 | //! 204 | //! #### Mixing _covariant_ and _contravariant_ types 205 | //! As a result of the flexibility discussed above, the following example of 206 | //! storing covariant and contravariant types in the same `Vec` is possible: 207 | //! ``` 208 | //! use transient::*; 209 | //! 210 | //! struct CoStruct<'a>(&'a i32); 211 | //! unsafe impl<'a> Transient for CoStruct<'a> { 212 | //! type Static = CoStruct<'static>; 213 | //! type Transience = Co<'a>; 214 | //! } 215 | //! 216 | //! struct ContraStruct<'a>(fn(&'a i32)); 217 | //! unsafe impl<'a> Transient for ContraStruct<'a> { 218 | //! type Static = ContraStruct<'static>; 219 | //! type Transience = Contra<'a>; 220 | //! } 221 | //! 222 | //! let value: i32 = 5; 223 | //! fn func(val: &i32) { dbg!(val); } 224 | //! 225 | //! // `co` could erase to `dyn Any`, but also `dyn Any` 226 | //! let co = Box::new(CoStruct(&value)); 227 | //! // `co` could erase to `dyn Any`, but also `dyn Any` 228 | //! let contra = Box::new(ContraStruct(func)); 229 | //! // the type annotation coerces both to choose the latter 230 | //! let erased_vec: Vec>> = vec![co, contra]; 231 | //! 232 | //! assert!(erased_vec[0].downcast_ref::().is_some()); 233 | //! assert!(erased_vec[1].downcast_ref::().is_some()); 234 | //! ``` 235 | //! Note however, that this technique is not _always_ possible; if you have a 236 | //! `CoStruct<'short>` and `ContraStruct<'long>`, there would be no common 237 | //! `Transience` for them to erase to; the first _cannot_ be lengthened to the 238 | //! `'long` lifetime due to its covariance, and the second _cannot_ be shortened 239 | //! to `'short` due to its contravariance. 240 | //! 241 | //! #### Mixing types with different numbers of lifetime parameters 242 | //! Type with more than one lifetime parameter can use a tuple containing a 243 | //! variance item for each lifetime as their `Transience`; this is discussed 244 | //! in-depth in the documentation for the [`Transience`] trait. Consider the 245 | //! following example defining two types, the first with a single lifetime 246 | //! and the second with two. We will choose _invariance_ for all lifetime 247 | //! parameters for simplicity, but when it comes to usage, a similar 248 | //! situation to the "mixed co- and contra-variance " example above arises; 249 | //! if we want to use the types together, we need to find a way to erase 250 | //! both types to a common `dyn Any<_>` trait object: 251 | //! ``` 252 | //! use transient::*; 253 | //! 254 | //! struct OneLifetime<'a>(&'a i32); 255 | //! unsafe impl<'a> Transient for OneLifetime<'a> { 256 | //! type Static = OneLifetime<'static>; 257 | //! type Transience = Inv<'a>; 258 | //! } 259 | //! 260 | //! struct TwoLifetimes<'a, 'b>(&'a i32, &'b i32); 261 | //! unsafe impl<'a, 'b> Transient for TwoLifetimes<'a, 'b> { 262 | //! type Static = TwoLifetimes<'static, 'static>; 263 | //! // we use a tuple for the `Transience` that covers both lifetimes 264 | //! type Transience = (Inv<'a>, Inv<'b>); 265 | //! } 266 | //! 267 | //! let (value1, value2) = (5, 7); 268 | //! // The "natural" choice would be erasing to `dyn Any` 269 | //! let one = Box::new(OneLifetime(&value1)); 270 | //! // The "natural" choice would be erasing to `dyn Any<(Inv, Inv)>` 271 | //! let two = Box::new(TwoLifetimes(&value1, &value2)); 272 | //! // The trait objects would not be compatible, but `one` can actually erase 273 | //! // to `dyn Any<(Inv, Inv)>` as well, since adding additional components is 274 | //! // allowed; so let's do that: 275 | //! let erased_vec: Vec>> = vec![one, two]; 276 | //! assert!(erased_vec[0].downcast_ref::().is_some()); 277 | //! assert!(erased_vec[1].downcast_ref::().is_some()); 278 | //! ``` 279 | //! 280 | //! In this example, we actually could have taken the opposite approach instead 281 | //! by coercing the types to `dyn Any>`, which would implicitly force `'a` 282 | //! and `'b` to be equal. In this case that would work since 'a and 'b are indeed 283 | //! equal: 284 | //! ``` 285 | //! # use transient::*; 286 | //! # struct OneLifetime<'a>(&'a i32); 287 | //! # unsafe impl<'a> Transient for OneLifetime<'a> { 288 | //! # type Static = OneLifetime<'static>; 289 | //! # type Transience = Inv<'a>; 290 | //! # } 291 | //! # struct TwoLifetimes<'a, 'b>(&'a i32, &'b i32); 292 | //! # unsafe impl<'a, 'b> Transient for TwoLifetimes<'a, 'b> { 293 | //! # type Static = TwoLifetimes<'static, 'static>; 294 | //! # type Transience = (Inv<'a>, Inv<'b>); 295 | //! # } 296 | //! let (value1, value2) = (5, 7); 297 | //! let one = Box::new(OneLifetime(&value1)); 298 | //! let two = Box::new(TwoLifetimes(&value1, &value2)); 299 | //! // allowed because 'a == 'b 300 | //! let erased_vec: Vec>> = vec![one, two]; 301 | //! assert!(erased_vec[0].downcast_ref::().is_some()); 302 | //! assert!(erased_vec[1].downcast_ref::().is_some()); 303 | //! ``` 304 | //! However if `'a `was `'short` and `'b` was `'long` then the invariance would 305 | //! prevent them from being unified. 306 | //! 307 | //! [`TypeId`]: TypeId 308 | //! [`transient::Any`]: Any 309 | //! [`Transient`]: tr::Transient 310 | //! [variance]: https://doc.rust-lang.org/nomicon/subtyping.html 311 | //! [_subtyping and variance_]: https://doc.rust-lang.org/nomicon/subtyping.html 312 | //! [*the quality or state of being transient*]: https://www.merriam-webster.com/dictionary/transience 313 | 314 | //////////////////////////////////////////////////////////////////////////////// 315 | 316 | // Support using `transient` without the standard library 317 | #![cfg_attr(not(feature = "std"), no_std)] 318 | #![deny(missing_docs, clippy::missing_safety_doc)] 319 | #![allow( 320 | unknown_lints, 321 | clippy::too_long_first_doc_paragraph, 322 | clippy::needless_lifetimes 323 | )] 324 | 325 | //////////////////////////////////////////////////////////////////////////////// 326 | 327 | #[cfg(feature = "alloc")] 328 | extern crate alloc; 329 | 330 | pub mod any; 331 | pub mod transience; 332 | 333 | // intentionally non-public to avoid naming conflicts with the crate 334 | mod transient; 335 | 336 | #[doc(inline)] 337 | pub use crate::any::{Any, Downcast, TypeId}; 338 | 339 | #[doc(inline)] 340 | pub use crate::transient::{Static, Transient}; 341 | 342 | #[doc(inline)] 343 | pub use transience::{Co, Contra, Inv, Timeless, Transience}; 344 | 345 | #[doc(inline)] 346 | pub use transience::{Contravariant, Covariant, Invariant, Lifetime}; 347 | 348 | pub use transience::{CanRecoverFrom, CanTranscendTo}; 349 | 350 | #[cfg(feature = "derive")] 351 | pub use transient_derive::Transient; 352 | 353 | /// Re-exports the [`Transient`] trait to enable unambiguously importing it 354 | /// instead of the [`Transient`][transient_derive::Transient] derive macro. 355 | pub mod tr { 356 | pub use super::transient::{Static, Transient}; 357 | } 358 | 359 | #[cfg(test)] 360 | pub mod tests; 361 | 362 | #[cfg(all(doctest, feature = "derive"))] 363 | #[doc = include_str!("../README.md")] 364 | struct ReadMe; 365 | -------------------------------------------------------------------------------- /src/any.rs: -------------------------------------------------------------------------------- 1 | //! Analogue to the [`std::any`] module, containing re-implementations of 2 | //! [`Any`] and [`TypeId`] that support non-`'static` types alongside 3 | //! re-exports of [`type_name`] and [`type_name_of_val`]. 4 | use crate::{ 5 | transience::{CanRecoverFrom, CanTranscendTo, Transience}, 6 | transient::Transient, 7 | }; 8 | 9 | #[cfg(feature = "alloc")] 10 | use alloc::boxed::Box; 11 | 12 | use core::marker::{Send, Sync}; 13 | 14 | /// Re-export from the [`std::any`] module. 15 | pub use core::any::type_name; 16 | 17 | /// Re-export from the [`std::any`] module. 18 | #[rustversion::since(1.76)] 19 | pub use core::any::type_name_of_val; 20 | 21 | /////////////////////////////////////////////////////////////////////////////// 22 | // `Any` trait 23 | /////////////////////////////////////////////////////////////////////////////// 24 | 25 | /// A trait to emulate dynamic typing, modeled after the [`std::any::Any`] trait with 26 | /// added support for non-`'static` types. 27 | /// 28 | /// This trait is primarily used as the `dyn Any` trait object, which has its 29 | /// methods defined on the [`Downcast`] extension trait. 30 | /// 31 | /// # Differences from `std::any::Any` 32 | /// - Types must first implement (or derive) the [`Transient`] trait before the 33 | /// blanket impl for all `T: Transient` will apply to them. 34 | /// - In addition to importing the `Any` trait, the [`Downcast`] trait must also 35 | /// be brought into scope for the `dyn Any` methods to become available. 36 | /// - Non-`'static` types can be erased by parameterizing the trait with the 37 | /// desired [`Transience`], which the compiler will ensure is compatible. Types 38 | /// that *are* `'static` can use any `Transience` they want, or exclude the 39 | /// parameter to use the default of `()`. 40 | /// - Not all `dyn Any<_>`'s are equal; the type parameter is considered to be 41 | /// be a part of the type, so `dyn Any>` is a different type than 42 | /// `dyn Any<()>` and they could not be stored together in the same `Vec`. To 43 | /// circumvent this limitation, a type `T` can be erased to any transience that 44 | /// is a *supertype* of `T::Transience`; for example, a `usize` can be erased 45 | /// to `dyn Any>` instead of the default `dyn Any<()>` so that it can 46 | /// be stored in a `Vec` with covariant types such as `&'a usize`. Note that if 47 | /// the transience is upcast to a shorter lifetime (or a longer lifetime in the 48 | /// *contravariant* case), then it can only be safely [`downcast`] to the 49 | /// shortened lifetime instead of the original (but if you are brave and/or 50 | /// careful, you can get around this using `unsafe` hacks like raw pointer 51 | /// casts and [`std::mem::transmute`]). 52 | /// - The [`Any::type_id`] method is difficult to use on concrete types as 53 | /// explained in its docstring; using [`TypeId::of_val`] instead. 54 | /// - The `*_unchecked` methods do not require nightly builds. 55 | /// - Only `Box`s using the `Global` allocator are supported. 56 | /// - Only `Sized` types are supported. 57 | /// 58 | /// This trait has a blanket `impl` for all [`Transient`] types with a compatible 59 | /// [`Transience`], and cannot be implemented directly. 60 | /// 61 | /// [`downcast`]: Downcast::downcast 62 | pub trait Any { 63 | /// Gets the `TypeId` of `self`, typically as an erased `dyn Any` trait object. 64 | /// 65 | /// Note that this method is much harder to use on a concrete type than the 66 | /// `std::any::Any::type_id` method, since the trait's generic parameter and 67 | /// blanket implementation means that any concrete type `T` actually has an 68 | /// entire family of `Any` implementations (one for each `Transience` type 69 | /// it can use to fill the `R` parameter), and the specific implementation 70 | /// to use would need to be specified explicitly using the fully qualified 71 | /// path (e.g. `>>::type_id(&value)`) even though they 72 | /// would all give the same result. To avoid this issue, you can instead use 73 | /// the [`TypeId::of_val`] function (e.g. `TypeId::of_val(&value)`) or the 74 | /// `Transient::static_type_id` method (e.g. `value.static_type_id()`) 75 | fn type_id(&self) -> TypeId; 76 | } 77 | 78 | impl Any for T 79 | where 80 | T::Transience: CanTranscendTo, 81 | { 82 | #[inline] 83 | fn type_id(&self) -> TypeId { 84 | TypeId::of::() 85 | } 86 | } 87 | 88 | /////////////////////////////////////////////////////////////////////////////// 89 | // `dyn Any` extension traits 90 | /////////////////////////////////////////////////////////////////////////////// 91 | 92 | /// Extension trait defining methods for downcasting the [`dyn Any<_>`][Any] trait 93 | /// object back into a concrete type. 94 | /// 95 | /// This trait has an implementation provided for the `dyn Any` trait object, 96 | /// as in not intended to be implemented by downstream types. 97 | pub trait Downcast { 98 | /// Returns `true` if the concrete type of the erased object is `T`, which can 99 | /// be used to predict the outcome of calling the [`downcast`][Self::downcast] 100 | /// and similar methods. 101 | /// 102 | /// Slight caveat: this method is _not actually_ comparing the erased type 103 | /// (call it `E`) to the given type `T`; in reality, it is comparing 104 | /// `E::Static` to `T::Static` as defined in their [`Transient`] impls. This 105 | /// is effectively equivalent for most purposes, but see the [`TypeId::of`] 106 | /// documentation for a discussion of the subtle differences (especially 107 | /// when using this check in the implementation of `unsafe` code). 108 | fn is(&self) -> bool; 109 | 110 | /// Attempt to downcast the box to a concrete type with its lifetime 111 | /// parameters restored, returning the original in the `Err` variant 112 | /// if the type was incorrect. 113 | #[cfg(feature = "alloc")] 114 | fn downcast(self: Box) -> Result, Box> 115 | where 116 | T::Transience: CanRecoverFrom; 117 | 118 | /// Returns a reference to the inner value with its lifetime parameters 119 | /// restored if it is of type `T`, or `None` if it isn't. 120 | fn downcast_ref(&self) -> Option<&T> 121 | where 122 | T::Transience: CanRecoverFrom; 123 | 124 | /// Returns a mutable reference to the inner value with its lifetime 125 | /// parameters restored if it is of type `T`, or `None` if it isn't. 126 | fn downcast_mut(&mut self) -> Option<&mut T> 127 | where 128 | T::Transience: CanRecoverFrom; 129 | 130 | /// Downcasts the box to a concrete type without compile-time checks. 131 | /// 132 | /// For a safe alternative see [`downcast`][Downcast::downcast]. 133 | /// 134 | /// # Safety 135 | /// The contained value must be of type `T::Static`; calling this method with 136 | /// the incorrect type is *undefined behavior*. However, the the caller is _not_ 137 | /// expected to uphold any lifetime guarantees, since the trait bounds handle 138 | /// this statically. 139 | #[cfg(feature = "alloc")] 140 | unsafe fn downcast_unchecked(self: Box) -> Box 141 | where 142 | T::Transience: CanRecoverFrom; 143 | 144 | /// Downcasts the shared reference to a concrete type without runtime checks. 145 | /// 146 | /// For a safe alternative see [`downcast_ref`][Downcast::downcast_ref]. 147 | /// 148 | /// # Safety 149 | /// The contained value must be of type `T::Static`; calling this method with 150 | /// the incorrect type is *undefined behavior*. However, the the caller is _not_ 151 | /// expected to uphold any lifetime guarantees, since the trait bounds handle 152 | /// this statically. 153 | unsafe fn downcast_ref_unchecked(&self) -> &T 154 | where 155 | T::Transience: CanRecoverFrom; 156 | 157 | /// Downcasts the mutable reference to a concrete type without runtime checks. 158 | /// 159 | /// For a safe alternative see [`downcast_mut`][Downcast::downcast_mut]. 160 | /// 161 | /// # Safety 162 | /// The contained value must be of type `T::Static`; calling this method with 163 | /// the incorrect type is *undefined behavior*. However, the the caller is _not_ 164 | /// expected to uphold any lifetime guarantees, since the trait bounds handle 165 | /// this statically. 166 | unsafe fn downcast_mut_unchecked(&mut self) -> &mut T 167 | where 168 | T::Transience: CanRecoverFrom; 169 | } 170 | 171 | macro_rules! dyn_any_impls { 172 | ($($for:tt)*) => { 173 | impl Downcast for dyn Any $($for)* + '_ { 174 | #[inline] 175 | fn is(&self) -> bool { 176 | self.type_id() == TypeId::of::() 177 | } 178 | 179 | #[cfg(feature = "alloc")] 180 | #[inline] 181 | fn downcast(self: Box) -> Result, Box> 182 | where 183 | T::Transience: CanRecoverFrom, 184 | { 185 | if >::is::(self.as_ref()) { 186 | // We just confirmed that the type is correct. 187 | Ok(unsafe { self.downcast_unchecked() }) 188 | } else { 189 | Err(self) 190 | } 191 | } 192 | 193 | #[inline] 194 | fn downcast_ref(&self) -> Option<&T> 195 | where 196 | T::Transience: CanRecoverFrom, 197 | { 198 | if >::is::(self) { 199 | // We just confirmed that the type is correct. 200 | Some(unsafe { self.downcast_ref_unchecked() }) 201 | } else { 202 | None 203 | } 204 | } 205 | 206 | #[inline] 207 | fn downcast_mut(&mut self) -> Option<&mut T> 208 | where 209 | T::Transience: CanRecoverFrom, 210 | { 211 | if >::is::(self) { 212 | // We just confirmed that the type is correct. 213 | Some(unsafe { self.downcast_mut_unchecked() }) 214 | } else { 215 | None 216 | } 217 | } 218 | 219 | #[cfg(feature = "alloc")] 220 | #[inline] 221 | unsafe fn downcast_unchecked(self: Box) -> Box 222 | where 223 | T::Transience: CanRecoverFrom, 224 | { 225 | // The caller is expected to ensure that the inner type is `T::Static`, 226 | // which the `Transient` trait guarantees has the same layout as `T`, 227 | // so the pointer cast is safe. The trait bound on `T::Transience` 228 | // ensures that the lifetime parameters of the returned type satisfy 229 | // the necessary subtyping relationships. 230 | Box::from_raw(Box::into_raw(self).cast()) 231 | } 232 | 233 | #[inline] 234 | unsafe fn downcast_ref_unchecked(&self) -> &T 235 | where 236 | T::Transience: CanRecoverFrom, 237 | { 238 | // The caller is expected to ensure that the inner type is `T::Static`, 239 | // which the `Transient` trait guarantees has the same layout as `T`, 240 | // so the pointer casts are safe. The trait bound on `T::Transience` 241 | // ensures that the lifetime parameters of the returned type satisfy 242 | // the necessary subtyping relationships. 243 | &*(self as *const Self).cast() 244 | } 245 | 246 | #[inline] 247 | unsafe fn downcast_mut_unchecked(&mut self) -> &mut T 248 | where 249 | T::Transience: CanRecoverFrom, 250 | { 251 | // The caller is expected to ensure that the inner type is `T::Static`, 252 | // which the `Transient` trait guarantees has the same layout as `T`, 253 | // so the pointer casts are safe. The trait bound on `T::Transience` 254 | // ensures that the lifetime parameters of the returned type satisfy 255 | // the necessary subtyping relationships. 256 | &mut *(self as *mut Self).cast() 257 | } 258 | } 259 | 260 | impl core::fmt::Debug for dyn Any $($for)* + '_ { 261 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 262 | f.write_str("dyn Any<")?; 263 | f.write_str(type_name::().rsplit("::").next().unwrap())?; 264 | let xtraits = stringify!($($for)*); 265 | if xtraits.len() > 0 { 266 | f.write_str("> ")?; 267 | f.write_str(xtraits) 268 | } else { 269 | f.write_str(">") 270 | } 271 | } 272 | } 273 | }; 274 | } 275 | 276 | dyn_any_impls!(); 277 | dyn_any_impls!(+ Send); 278 | dyn_any_impls!(+ Send + Sync); 279 | 280 | /////////////////////////////////////////////////////////////////////////////// 281 | // `TypeID` and its methods 282 | /////////////////////////////////////////////////////////////////////////////// 283 | 284 | /// Thin wrapper for [`std::any::TypeId`], which represents a globally unique 285 | /// identifier for a type. 286 | /// 287 | /// Each `TypeId` is an opaque object which does not allow inspection of what's 288 | /// inside but does allow basic operations such as cloning, comparison, 289 | /// printing, and showing. 290 | /// 291 | /// While the `std::any::TypeId` type is currently only available for `'static` 292 | /// types, this wrapped version is instead provided for any type implementing 293 | /// the [`Transient`] trait defined in this crate by simply querying the `TypeId` 294 | /// of the `Static` associated type defined in its `Transient` impl. 295 | /// 296 | /// A slight caveat of this implementation is that this `TypeId` for some type 297 | /// `T: Transient` is _technically_ the unique identifier for [`T::Static`] as 298 | /// defined in its `Transient` impl instead of `T` itself, but as long as the 299 | /// `Transient` trait was implemented correctly (which the `unsafe` implementor 300 | /// pinky-promised they did), then this "static identity" is effectively equivalent. 301 | /// However, this identifier ignores all lifetime information about the type, 302 | /// `&'short str` will have the same `TypeId` as `&'static str`, and `unsafe` 303 | /// code **should not** assume that it can ignore lifetimes based on the `TypeId` 304 | /// alone. 305 | /// 306 | /// Quoting from the `std::any::TypeId` documentation: while `TypeId` implements 307 | /// `Hash`, `PartialOrd`, and `Ord`, it is worth noting that the hashes and ordering 308 | /// will vary between Rust releases. Beware of relying on them inside of your code! 309 | /// 310 | /// # Examples 311 | /// ``` 312 | /// use transient::{TypeId, Any, Transient}; 313 | /// 314 | /// let static_str = "cookie_monster"; 315 | /// // 'static types have a `TypeId` just like in the `std::any` module (as long 316 | /// // as they implement the `Transient` trait, which &'static str does); however, 317 | /// // we use the `Transient::static_type_id` method or `TypeId::of_val` function 318 | /// // instead of `Any::type_id` when dealing with concrete types to avoid needing 319 | /// // to use the fully qualified path (see `Any::type_id` docs). 320 | /// assert_eq!(static_str.static_type_id(), TypeId::of::<&'static str>()); 321 | /// assert_eq!(TypeId::of_val(&static_str), TypeId::of::<&'static str>()); 322 | /// { 323 | /// let temp_string = static_str.to_string(); 324 | /// let temp_str: &str = &temp_string; 325 | /// // unlike `std::any`, non-'static types also have a `TypeId` 326 | /// assert_eq!(temp_str.static_type_id(), TypeId::of::<&'_ str>()); 327 | /// // note that this `TypeId` will match regardless of the lifetime 328 | /// assert_eq!(temp_str.static_type_id(), TypeId::of::<&'static str>()); 329 | /// } 330 | /// // this `TypeId` can also be compared to a `std::any::TypeId` 331 | /// assert_eq!(TypeId::of::<&'_ str>(), std::any::TypeId::of::<&'static str>()); 332 | /// ``` 333 | /// [`T::Static`]: Transient::Static 334 | #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] 335 | pub struct TypeId(core::any::TypeId); 336 | 337 | impl TypeId { 338 | /// Returns the `TypeId` of the [`Transient`] type this generic function 339 | /// has been instantiated with. 340 | /// 341 | /// See the [`TypeId`] documentation for a discussion of the subtle differences 342 | /// between this identifier and the `std::any::TypeId`. 343 | /// 344 | /// # Examples 345 | /// 346 | /// ``` 347 | /// # #[cfg(any(feature = "std", feature = "alloc"))] { 348 | /// use transient::{Transient, Any, TypeId}; 349 | /// 350 | /// fn is_string_slice(_s: &T) -> bool { 351 | /// TypeId::of::<&str>() == TypeId::of::() 352 | /// } 353 | /// 354 | /// let string = "cookie monster".to_string(); 355 | /// let string_slice: &str = &string; 356 | /// 357 | /// assert_eq!(is_string_slice(&0), false); 358 | /// assert_eq!(is_string_slice(&string), false); 359 | /// assert_eq!(is_string_slice(&string_slice), true); 360 | /// assert_eq!(is_string_slice(&"cookie monster"), true); 361 | /// # } 362 | /// ``` 363 | #[inline] 364 | pub fn of() -> Self { 365 | let () = T::CHECK; 366 | TypeId(core::any::TypeId::of::()) 367 | } 368 | 369 | /// Returns the `TypeId` for the type of the given value. 370 | /// 371 | /// This is effectively the same as [`TypeId::of`], but allows a value to 372 | /// provided so that type inference can be used to get the type `T` instead 373 | /// of needing to explicitly specify it. 374 | /// 375 | /// See the [`TypeId`] documentation for a discussion of the subtle differences 376 | /// between this identifier and the [`std::any::TypeId`]. 377 | #[inline] 378 | pub fn of_val(_value: &T) -> Self { 379 | TypeId::of::() 380 | } 381 | } 382 | 383 | impl From for TypeId { 384 | #[inline] 385 | fn from(value: core::any::TypeId) -> Self { 386 | TypeId(value) 387 | } 388 | } 389 | 390 | impl From for core::any::TypeId { 391 | #[inline] 392 | fn from(value: TypeId) -> Self { 393 | value.0 394 | } 395 | } 396 | 397 | impl PartialEq for TypeId { 398 | #[inline] 399 | fn eq(&self, other: &core::any::TypeId) -> bool { 400 | self.0.eq(other) 401 | } 402 | } 403 | 404 | impl core::fmt::Debug for TypeId { 405 | #[inline] 406 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 407 | self.0.fmt(f) 408 | } 409 | } 410 | 411 | impl core::hash::Hash for TypeId { 412 | #[inline] 413 | fn hash(&self, state: &mut H) { 414 | self.0.hash(state) 415 | } 416 | } 417 | 418 | #[cfg(test)] 419 | // FIXME: move to crate::tests 420 | mod tests { 421 | use crate::{tr::Transient, Any, Co, Downcast, Inv}; 422 | 423 | #[cfg(feature = "alloc")] 424 | use alloc::{boxed::Box, format}; 425 | 426 | #[test] 427 | #[cfg(feature = "alloc")] 428 | fn owned_primative_types() { 429 | let value = 5_usize; 430 | let valref: &usize = &value; 431 | let valrefref = &valref; 432 | 433 | // owned `usize` 434 | let ts: Box> = Box::new(5_usize); 435 | let co: Box> = Box::new(5_usize); 436 | let inv: Box> = Box::new(5_usize); 437 | assert_eq!(*ts.downcast::().unwrap(), value); 438 | assert_eq!(*co.downcast::().unwrap(), value); 439 | assert_eq!(*inv.downcast::().unwrap(), value); 440 | 441 | // owned `&usize` 442 | let co: Box> = Box::new(valref); 443 | let inv: Box> = Box::new(valref); 444 | assert_eq!(*co.downcast::<&usize>().unwrap(), valref); 445 | assert_eq!(*inv.downcast::<&usize>().unwrap(), valref); 446 | 447 | // owned `&&usize` 448 | let inv_inv: Box> = Box::new(valrefref); 449 | let inv_co: Box> = Box::new(valrefref); 450 | let co_inv: Box> = Box::new(valrefref); 451 | let co_co: Box> = Box::new(valrefref); 452 | let co: Box> = Box::new(valrefref); 453 | let inv: Box> = Box::new(valrefref); 454 | assert_eq!(*inv_inv.downcast::<&&usize>().unwrap(), valrefref); 455 | assert_eq!(*inv_co.downcast::<&&usize>().unwrap(), valrefref); 456 | assert_eq!(*co_inv.downcast::<&&usize>().unwrap(), valrefref); 457 | assert_eq!(*co_co.downcast::<&&usize>().unwrap(), valrefref); 458 | assert_eq!(*co.downcast::<&&usize>().unwrap(), valrefref); 459 | assert_eq!(*inv.downcast::<&&usize>().unwrap(), valrefref); 460 | 461 | // owned `&mut &mut usize` 462 | let mut value = 5_usize; 463 | let mut valmut = &mut value; 464 | let inv_inv: Box> = Box::new(&mut valmut); 465 | assert_eq!( 466 | *inv_inv.downcast::<&mut &mut usize>().unwrap(), 467 | &mut &mut 5usize 468 | ); 469 | let co_inv: Box> = Box::new(&mut valmut); 470 | assert_eq!( 471 | *co_inv.downcast::<&mut &mut usize>().unwrap(), 472 | &mut &mut 5usize 473 | ); 474 | let inv: Box> = Box::new(&mut valmut); 475 | assert_eq!( 476 | *inv.downcast::<&mut &mut usize>().unwrap(), 477 | &mut &mut 5usize 478 | ); 479 | } 480 | 481 | #[test] 482 | fn borrowed_primative_types() { 483 | let val: i32 = 5; 484 | let valref = &val; 485 | let valrefref = &valref; 486 | 487 | let erased: [&dyn Any; 3] = [&5, &valref, &valrefref]; 488 | assert_eq!(erased[0].downcast_ref::().unwrap(), &5); 489 | assert_eq!(erased[1].downcast_ref::<&i32>().unwrap(), &valref); 490 | assert_eq!(erased[2].downcast_ref::<&&i32>().unwrap(), &valrefref); 491 | 492 | // test values 493 | let value = 5_usize; 494 | let valref: &usize = &value; 495 | let valrefref = &valref; 496 | 497 | // borrowed `usize` 498 | let ts: &dyn Any = &value; 499 | let co: &dyn Any = &value; 500 | let inv: &dyn Any = &value; 501 | assert_eq!(ts.downcast_ref::().unwrap(), &value); 502 | assert_eq!(co.downcast_ref::().unwrap(), &value); 503 | assert_eq!(inv.downcast_ref::().unwrap(), &value); 504 | 505 | // borrowed `&usize` 506 | let co: &dyn Any = &valref; 507 | let inv: &dyn Any = &valref; 508 | assert_eq!(co.downcast_ref::<&usize>().unwrap(), &valref); 509 | assert_eq!(inv.downcast_ref::<&usize>().unwrap(), &valref); 510 | 511 | // borrowed `&&usize` 512 | let inv_inv: &dyn Any<(Inv, Inv)> = &valrefref; 513 | let co_inv: &dyn Any<(Co, Inv)> = &valrefref; 514 | let inv_co: &dyn Any<(Inv, Co)> = &valrefref; 515 | let co_co: &dyn Any<(Co, Co)> = &valrefref; 516 | let co: &dyn Any = &valrefref; 517 | let inv: &dyn Any = &valrefref; 518 | assert_eq!(inv_inv.downcast_ref::<&&usize>().unwrap(), &valrefref); 519 | assert_eq!(co_inv.downcast_ref::<&&usize>().unwrap(), &valrefref); 520 | assert_eq!(inv_co.downcast_ref::<&&usize>().unwrap(), &valrefref); 521 | assert_eq!(co_co.downcast_ref::<&&usize>().unwrap(), &valrefref); 522 | assert_eq!(co.downcast_ref::<&&usize>().unwrap(), &valrefref); 523 | assert_eq!(inv.downcast_ref::<&&usize>().unwrap(), &valrefref); 524 | 525 | // borrowed `&mut &mut usize` 526 | let mut value = 5_usize; 527 | let mut valmut = &mut value; 528 | let valmutmut = &mut valmut; 529 | let inv_inv: &dyn Any<(Inv, Inv)> = &valmutmut; 530 | assert_eq!( 531 | inv_inv.downcast_ref::<&mut &mut usize>().unwrap(), 532 | &&mut &mut 5usize 533 | ); 534 | let co_inv: &dyn Any<(Co, Inv)> = &valmutmut; 535 | assert_eq!( 536 | co_inv.downcast_ref::<&mut &mut usize>().unwrap(), 537 | &&mut &mut 5usize 538 | ); 539 | let inv: &dyn Any = &valmutmut; 540 | assert_eq!( 541 | inv.downcast_ref::<&mut &mut usize>().unwrap(), 542 | &&mut &mut 5usize 543 | ); 544 | } 545 | 546 | #[derive(Debug, Clone)] 547 | pub struct Usize(usize); 548 | 549 | unsafe impl Transient for Usize { 550 | type Static = Usize; 551 | type Transience = (); 552 | } 553 | 554 | #[derive(Debug, Clone, Copy)] 555 | pub struct UsizeRef<'a>(&'a usize); 556 | 557 | unsafe impl<'a> Transient for UsizeRef<'a> { 558 | type Static = UsizeRef<'static>; 559 | type Transience = Co<'a>; 560 | } 561 | 562 | #[test] 563 | #[cfg(feature = "alloc")] 564 | fn owned_custom_types() { 565 | let usize_ = Usize(5_usize); 566 | let usize_ref = UsizeRef(&usize_.0); 567 | 568 | // owned `Usize` 569 | let stc: Box> = Box::new(usize_.clone()); 570 | let inv: Box> = Box::new(usize_.clone()); 571 | let co: Box> = Box::new(usize_.clone()); 572 | assert_eq!(stc.downcast::().unwrap().0, 5_usize); 573 | assert_eq!(inv.downcast::().unwrap().0, 5_usize); 574 | assert_eq!(co.downcast::().unwrap().0, 5_usize); 575 | 576 | // owned `UsizeRef` 577 | let inv: Box> = Box::new(usize_ref.clone()); 578 | let co: Box> = Box::new(usize_ref.clone()); 579 | assert_eq!(inv.downcast::().unwrap().0, &5_usize); 580 | assert_eq!(co.downcast::().unwrap().0, &5_usize); 581 | 582 | // owned `UsizeRef` + Send 583 | let inv: Box + Send> = Box::new(usize_ref.clone()); 584 | let co: Box + Send> = Box::new(usize_ref.clone()); 585 | assert_eq!(inv.downcast::().unwrap().0, &5_usize); 586 | assert_eq!(co.downcast::().unwrap().0, &5_usize); 587 | 588 | // owned `UsizeRef` + Send + Sync 589 | let usize_ref = UsizeRef(&usize_.0); 590 | let inv: Box + Send + Sync> = Box::new(usize_ref.clone()); 591 | let co: Box + Send + Sync> = Box::new(usize_ref.clone()); 592 | assert_eq!(inv.downcast::().unwrap().0, &5_usize); 593 | assert_eq!(co.downcast::().unwrap().0, &5_usize); 594 | } 595 | 596 | #[test] 597 | fn borrowed_custom_types() { 598 | let usize_ = Usize(5_usize); 599 | let usize_ref = UsizeRef(&usize_.0); 600 | 601 | // borrowed `Usize` 602 | let stc: &dyn Any<()> = &usize_; 603 | let inv: &dyn Any = &usize_; 604 | let co: &dyn Any = &usize_; 605 | assert_eq!(stc.downcast_ref::().unwrap().0, 5_usize); 606 | assert_eq!(inv.downcast_ref::().unwrap().0, 5_usize); 607 | assert_eq!(co.downcast_ref::().unwrap().0, 5_usize); 608 | 609 | #[cfg(feature = "alloc")] 610 | assert_eq!(&format!("{:?}", stc), "dyn Any<()>"); 611 | 612 | // borrowed `UsizeRef` 613 | let inv: &dyn Any = &usize_ref; 614 | let co: &dyn Any = &usize_ref; 615 | assert_eq!(inv.downcast_ref::().unwrap().0, &5_usize); 616 | assert_eq!(co.downcast_ref::().unwrap().0, &5_usize); 617 | #[cfg(feature = "alloc")] 618 | assert_eq!(&format!("{:?}", co), "dyn Any"); 619 | 620 | // borrowed `UsizeRef` + Send 621 | let inv: &(dyn Any + Send) = &usize_ref; 622 | let co: &(dyn Any + Send) = &usize_ref; 623 | assert_eq!(inv.downcast_ref::().unwrap().0, &5_usize); 624 | assert_eq!(co.downcast_ref::().unwrap().0, &5_usize); 625 | #[cfg(feature = "alloc")] 626 | assert_eq!(&format!("{:?}", co), "dyn Any + Send"); 627 | 628 | // borrowed `UsizeRef` + Send + Sync 629 | let inv: &(dyn Any + Send + Sync) = &usize_ref; 630 | let co: &(dyn Any + Send + Sync) = &usize_ref; 631 | assert_eq!(inv.downcast_ref::().unwrap().0, &5_usize); 632 | assert_eq!(co.downcast_ref::().unwrap().0, &5_usize); 633 | #[cfg(feature = "alloc")] 634 | assert_eq!(&format!("{:?}", inv), "dyn Any + Send + Sync") 635 | } 636 | } 637 | -------------------------------------------------------------------------------- /src/transient.rs: -------------------------------------------------------------------------------- 1 | //! Defines the [`Transient`] trait capturing the temporal information necessary 2 | //! to safely erase and restore non-`'static` concrete types. 3 | use crate::any::{Any, TypeId}; 4 | use crate::transience::Transience; 5 | 6 | #[cfg(feature = "alloc")] 7 | use alloc::boxed::Box; 8 | 9 | /// Unsafe trait defining the lifetime-relationships of a potentially non-`'static` 10 | /// type so that it can be safely erased to [`dyn Any`]. This trait can be safely 11 | /// derived for most types using the [`Transient` derive macro]. 12 | /// 13 | /// Note that `'static` types with no lifetime parameters can instead implement the 14 | /// _safe_ [`Static`] trait to get a free blanket implementation of this trait. 15 | /// 16 | /// # Implementing the `Transient` trait 17 | /// Implementing the [`Transient`] trait only requires the definition of two 18 | /// associated types: the [`Static`][Self::Static] type, and the 19 | /// [`Transience`][Self::Transience]. The requirements for safely choosing 20 | /// these types are explained in the following subsections. 21 | /// 22 | /// ## The `Static` associated type 23 | /// This type should be the same as the `Self` type but with all lifetime 24 | /// parameters replaced by `'static`. See the [next section][Self#Examples] 25 | /// for numerous examples and a discussion of the few non-trivial cases such 26 | /// types with non-`'static` generic type parameters. 27 | /// 28 | /// ## The `Transience` associated type 29 | /// This type must be a [`Transience`] implementation that properly captures the 30 | /// _variance_ of the type with respect to each of its lifetime parameters. The 31 | /// safe implementation of this types is a bit more nuanced than the `Static` 32 | /// type, and differs depending on the number of lifetime parameters for the type 33 | /// as laid out in the following subsections. 34 | /// 35 | /// #### Types with 0 lifetime parameters 36 | /// These types are the easiest to implement and use since they contain no borrowed 37 | /// data to worry about, and can simply use the unit type `()` (or the [`Timeless`] 38 | /// type alias) as their `Transience`. 39 | /// 40 | /// #### Types with exactly 1 lifetime parameter 41 | /// For a type with a single lifetime parameter `'a`, there are 3 main options 42 | /// which correspond to the standard [variances] that a type can have: 43 | /// - [`Inv<'a>`] -- this type declares that the implementing type is _invariant_ 44 | /// with respect to `'a`, which means that the compiler can neither shorten nor 45 | /// length its lifetime freely. This is the most conservative form of variance, 46 | /// and can safely be chosen for the `Transience` of any single-lifetime type. 47 | /// However, this variance is the least flexible when it comes to usage; in a 48 | /// world were `&'a str` was considered _invariant_ you would not be allowed 49 | /// to pass a `&'static str` to a function expecting `&'short str`, even though 50 | /// the former should clearly be more capable than the latter. 51 | /// - [`Co<'a>`] -- this type declares that the implementing type is _covariant_ 52 | /// with respect to `'a`, which means that the compiler can safely shorten its 53 | /// lifetime as needed, but cannot lengthen it. _Most_ types exhibit this variance 54 | /// and can use it for their `Transience` (such as `&'a str` discussed above), 55 | /// but using it in the few cases where it does _not_ apply could result in 56 | /// undefined behavior (and so covariance cannot be the default). Notable 57 | /// exceptions from _covariant_ behavior include the argument of a function 58 | /// pointer (a type containing an `fn(&'a str)` is _contravariant_ w.r.t. `'a`) 59 | /// and the pointee of a mutable reference (`&'a mut &'b str` is _invariant_ 60 | /// w.r.t. `'b`, although it is still _covariant_ w.r.t. `'a`). 61 | /// - [`Contra<'a>`] -- this type declares that the implementing type is 62 | /// _contravariant_ with respect to `'a`, which means that the compiler can 63 | /// safely _lengthen_ its lifetime as needed, but cannot shorten it. This is 64 | /// the least common variance so I won't discuss it in depth, but the main 65 | /// example of contravariance is the relationship between a function and 66 | /// the lifetime parameters of its arguments (such as `fn(&'a str)` as 67 | /// mentioned above. 68 | /// 69 | /// As a side note, a 1-tuple containing `Inv`, `Co`, or `Contra` can 70 | /// also be used for the `Transience` of a single-lifetime type (subject 71 | /// to the same rules as above), but this is typically less convenient 72 | /// when it comes to usage. 73 | /// 74 | /// #### Types with more than 1 lifetime parameter 75 | /// Choosing a `Transience` for a type with multiple lifetime parameters 76 | /// is really no harder than for a single-lifetime types, since the same 77 | /// variances discussed above can simply be composed as tuples with an 78 | /// independent element corresponding to each lifetime. For example, 79 | /// _any_ type with two lifetimes `'a` and `'b` can safely choose 80 | /// `(Inv<'a>, Inv<'b>)` as its `Transience` since the independent 81 | /// choices of `Inv<'a>` and `Inv<'b>` are always safe. For a type like 82 | /// `&'a mut &'b str` which is is _covariant_ w.r.t. to `'a` but 83 | /// _invariant_ w.r.t. `b`, the `Transience` could be defined as 84 | /// `(Co<'a>, Inv<'b>)`. Just make sure to include a tuple element for 85 | /// __every lifetime__ in the struct (choosing `Inv<'_>` for the variance 86 | /// when unsure), since any excluded lifetimes will be unbounded and can 87 | /// lead to undefined behavior. 88 | /// 89 | /// Note that this pattern of composing tuples should in theory hold 90 | /// for _any_ number of lifetimes (i.e. a type with 100 lifetimes using 91 | /// a 100-tuple of variances), but in practice the `Transience` trait 92 | /// is only actually implemented for 1-, 2-, 3-, and 4-tuples. If you 93 | /// need more than this feel free to submit an issue requesting it. 94 | /// 95 | /// # Examples 96 | /// Note: The following examples demonstrate how to correctly implement this trait. 97 | /// For practical usage examples, see the [crate documentation][crate#examples]. 98 | /// 99 | /// ## Static types 100 | /// The simplest case of implementing this trait is for a struct that is already 101 | /// `'static` (i.e. it only contains owned data and/or `'static` references. For 102 | /// such a struct, the `Static` type can simply be `Self`, and the `Transience` 103 | /// type can be the unit type `()` (or the type alias `transient::Timeless`): 104 | /// ``` 105 | /// use transient::Transient; 106 | /// struct S { 107 | /// name: &'static str, 108 | /// value: i32 109 | /// } 110 | /// unsafe impl Transient for S { 111 | /// type Static = Self; 112 | /// type Transience = (); 113 | /// } 114 | /// ``` 115 | /// 116 | /// Of course, this crate would not be necessary in this case, but it is still 117 | /// worth mentioning that `'static` types are indeed supported. 118 | /// 119 | /// ## Types with a single lifetime parameter 120 | /// 121 | /// The next simplest case would be a struct with a single lifetime parameter 122 | /// and no generic type parameters: 123 | /// ``` 124 | /// use transient::{Transient, Inv}; 125 | /// struct S<'a> { 126 | /// value: &'a str, 127 | /// } 128 | /// // This could also be derived 129 | /// unsafe impl<'a> Transient for S<'a> { 130 | /// type Static = S<'static>; 131 | /// type Transience = Inv<'a>; 132 | /// } 133 | /// ``` 134 | /// 135 | /// ## Types with multiple lifetime parameters 136 | /// 137 | /// Now consider a struct that borrows 2 string slices with independent 138 | /// lifetime parameters (which is currently not supported by the derive 139 | /// macro): 140 | /// ``` 141 | /// struct TwoRefs<'a, 'b> { 142 | /// a: &'a str, 143 | /// b: &'b str, 144 | /// } 145 | /// ``` 146 | /// 147 | /// There are several options for how to safely implement the `Transient` trait 148 | /// for such a type. The most versatile option is to follow the same pattern as 149 | /// for the single-lifetime example, but to use a _tuple_ for the `Transience` 150 | /// type that contains a separate `Transience` for each lifetime: 151 | /// ``` 152 | /// # use transient::{Transient, Inv}; 153 | /// # struct TwoRefs<'a, 'b> {a: &'a str, b: &'b str} 154 | /// unsafe impl<'a, 'b> Transient for TwoRefs<'a, 'b> { 155 | /// type Static = TwoRefs<'static, 'static>; 156 | /// type Transience = (Inv<'a>, Inv<'b>); 157 | /// } 158 | /// ``` 159 | /// 160 | /// Another option is to establish a relationship between the lifetimes that allows 161 | /// a most conservative `Transience` to be unambiguously identified for use in the impl: 162 | /// ``` 163 | /// # use transient::{Transient, Inv}; 164 | /// # struct TwoRefs<'a, 'b> {a: &'a str, b: &'b str} 165 | /// // 'b outlives 'a -> choose 'a for the trait 166 | /// unsafe impl<'a, 'b: 'a> Transient for TwoRefs<'a, 'b> { 167 | /// type Static = TwoRefs<'static, 'static>; 168 | /// type Transience = Inv<'a>; 169 | /// } 170 | /// ``` 171 | /// 172 | /// This can make using the `dyn transient::Any` trait object more convenient 173 | /// in some cases, but will result in the lifetime of the restored type being 174 | /// truncated to the chosen lifetime. 175 | /// 176 | /// However, choosing either `'a` **or** `'b` for the trait without declaring 177 | /// bounds to justify the decision is *unsound* and may lead to undefined 178 | /// behaviour. 179 | /// 180 | /// ## Generic type parameters 181 | /// 182 | /// Generic type parameters are also supported; however, there is one extra 183 | /// consideration to keep in mind. Since the `Static` associated type on the 184 | /// `Transient` trait is bounded by `'static`, any generics parameters that 185 | /// appear in this type must also be `'static`. The easiest way to meet this 186 | /// condition is to directly bound the type parameters by `'static` for the 187 | /// impl block, as shown in the following example; however, there is workaround 188 | /// for cases where this is not acceptable, which will be shown next. 189 | /// 190 | /// For the case where the type parameters can be `'static`: 191 | /// ``` 192 | /// use transient::{Transient, Inv}; 193 | /// // This struct is generic over type `T`, which might not be `'static` 194 | /// struct S<'a, T> { 195 | /// value: &'a T, 196 | /// } 197 | /// // By adding `T: 'static` to the impl block we can satisfy the `'static` 198 | /// // requirement, at the cost of limiting the scope of the impl 199 | /// unsafe impl<'a, T: 'static> Transient for S<'a, T> { 200 | /// type Static = S<'static, T>; 201 | /// type Transience = Inv<'a>; 202 | /// } 203 | /// ``` 204 | /// 205 | /// If you need to support cases where `T` is not necessarily `'static`, another 206 | /// option is to bound `T` by `Transient` itself and then using `T::Static` in 207 | /// the `Static` type for the impl: 208 | /// ``` 209 | /// use transient::{Transient, Inv}; 210 | /// 211 | /// struct S<'a, T> { 212 | /// value: &'a T, 213 | /// } 214 | /// 215 | /// unsafe impl<'a, T: Transient> Transient for S<'a, T> { 216 | /// type Static = S<'static, T::Static>; 217 | /// type Transience = Inv<'a>; 218 | /// } 219 | /// ``` 220 | /// Of course, this limits the impl to types where `Transient` either _is_ or 221 | /// _can be_ implemented. If you need to support external types for which you 222 | /// cannot implement `Transient` due to the orphan rule, your only options 223 | /// would be to wrap `T` in a newtype struct for which you can implement 224 | /// `Transient`, or request that the impl be added by this crate or the type's 225 | /// crate. 226 | /// 227 | /// # Safety 228 | /// - The [`Static`][Self::Static] associated type must be the same type as the 229 | /// implementing type, but with all lifetime parameters replaced by `'static` and 230 | /// any non-`'static`-bounded type parameters `T` replaced by `T::Static` (for 231 | /// which they must be bounded by `Transient`). Specifically, the type must have 232 | /// the same layout as `Self` so that `std::mem::transmute` and raw pointer casts 233 | /// pointer casts between them are sound, and the `std::any::TypeId` of the 234 | /// `Static` type must correctly identify the `Self` type. 235 | /// - The [`Transience`][Self::Transience] associate type must include a component 236 | /// for each lifetime parameter that accurately (or more conservatively) captures 237 | /// the `Self` type's variance with respect to it, as detailed in the documentation 238 | /// for the [`Transience`] trait and demonstrated in the sections above. For a 239 | /// `'static` type this should be `()`, for a single-lifetime type it should be 240 | /// [`Inv<'a>`] as a safe default or [`Co<'a>`]/[`Contra<'a>`] if appropriate, 241 | /// and for a multi-lifetime type this should be `(Inv<'a>, Inv<'b>, ...)` as a 242 | /// safe default with `Co` and `Contra` optionally substituted where appropriate. 243 | /// Choosing `Co` or `Contra` for any lifetime parameter without respecting the 244 | /// rules of [Subtyping and Variance], or excluding any independent lifetime 245 | /// parameter from the `Transience` is undefined behavior. 246 | /// 247 | /// [`dyn Any`]: Any 248 | /// [`Timeless`]: crate::transience::Timeless 249 | /// [`Inv<'a>`]: crate::transience::Inv 250 | /// [`Co<'a>`]: crate::transience::Co 251 | /// [`Contra<'a>`]: crate::transience::Contra 252 | /// [`erase`]: Transient::erase 253 | /// [`erase_ref`]: Transient::erase_ref 254 | /// [`erase_mut`]: Transient::erase_mut 255 | /// [`Transient` derive macro]: transient_derive::Transient 256 | /// [variances]: https://doc.rust-lang.org/nomicon/subtyping.html 257 | /// [Subtyping and Variance]: https://doc.rust-lang.org/nomicon/subtyping.html 258 | pub unsafe trait Transient: Sized { 259 | /// Same as `Self` but with all lifetime parameters replaced by `'static`. 260 | /// 261 | /// See the [`Transient`] trait's docstring for examples and a discussion of 262 | /// the considerations necessary for defining the type in various cases. 263 | /// 264 | /// # Safety 265 | /// This must be equivalent to the implementing type, such that matching its 266 | /// [`TypeId`] to that of a `dyn Any` trait objects is sufficient justification 267 | /// for performing a [`std::mem::transmute`] or raw pointer cast to it 268 | /// (excluding lifetime considerations). 269 | type Static: 'static; 270 | 271 | /// Type reflecting the variances of `Self` with respect to its lifetime parameters. 272 | /// 273 | /// See the [`Transience`] docstring for a thorough explanation and examples. 274 | /// 275 | /// # Safety 276 | /// This type must sufficiently capture the _variance_ characteristics of the 277 | /// type with respect to every one of its lifetime parameters as discussed in 278 | /// the documentation for the trait. 279 | type Transience: Transience; 280 | 281 | #[doc(hidden)] 282 | // attempts to validate the `Static` type and give a better error message if 283 | // set incorrectly, but this is not exhaustive and must be used to take effect 284 | const CHECK: () = check_static_type::(); 285 | 286 | /// Obtain the unique identifier assigned by the compiler to the 287 | /// [`Static`][Self::Static] variant of the type. 288 | /// 289 | /// See the docstring for the [`TypeId`] type for a discussion of the subtle 290 | /// differences from the related [`std::any::TypeId`], and the [`Any::type_id`] 291 | /// method for an explanation of why this method is necessary. 292 | /// 293 | /// See [`TypeId::of_val`] for an alternate method of obtaining the `TypeId` 294 | /// for a value with a concrete type. 295 | #[inline] 296 | fn static_type_id(&self) -> TypeId { 297 | TypeId::of::() 298 | } 299 | 300 | /// Convenience method to cast `Box` to `Box>` with the 301 | /// transience defined in the `Transient` implementation. 302 | /// 303 | /// This shorthand can be useful since the default `dyn Any` only works for 304 | /// `'static` types, so `Transient` types would need to import the appropriate 305 | /// `Transience` type (such as [`Co`][crate::Co]) and explicitly specify 306 | /// `dyn Any` even for trivial usages (although using `dyn Any<_>` and 307 | /// letting type-inference fill-in-the-blank will also work in some cases). 308 | #[cfg(any(feature = "std", feature = "alloc"))] 309 | #[inline] 310 | fn erase<'a>(self: Box) -> Box + 'a> 311 | where 312 | Self: 'a, 313 | { 314 | let () = Self::CHECK; 315 | self 316 | } 317 | 318 | /// Convenience method to cast `&Self` to `&dyn Any<_>` with the 319 | /// transience defined in the `Transient` implementation. 320 | #[inline] 321 | fn erase_ref<'a>(&self) -> &(dyn Any + 'a) 322 | where 323 | Self: 'a, 324 | { 325 | let () = Self::CHECK; 326 | self 327 | } 328 | 329 | /// Convenience method to cast `&mut Self` to `&mut dyn Any<_>` with the 330 | /// transience defined in the `Transient` implementation. 331 | #[inline] 332 | fn erase_mut<'a>(&mut self) -> &mut (dyn Any + 'a) 333 | where 334 | Self: 'a, 335 | { 336 | let () = Self::CHECK; 337 | self 338 | } 339 | } 340 | 341 | /// Safe trait that `'static` types can implement to get a free blanket impl 342 | /// of the [`Transient`] trait. 343 | /// 344 | /// Implementing this trait results in a `Transient` implementation using `Self` 345 | /// as the `Static` type and `()` as the `Transience`, which is almost certainly 346 | /// what a `'static` type would want. 347 | pub trait Static: 'static + Sized {} 348 | 349 | unsafe impl Transient for S { 350 | type Static = Self; 351 | type Transience = (); 352 | } 353 | 354 | #[track_caller] 355 | const fn check_static_type() { 356 | use core::{alloc::Layout, mem::size_of}; 357 | 358 | assert!( 359 | size_of::() == size_of::(), 360 | "Size mismatch! `T::Static` should be the same as `T` \ 361 | but with its lifetimes replaced by `'static`" 362 | ); 363 | assert!( 364 | Layout::new::().align() == Layout::new::().align(), 365 | "Alignment mismatch! `T::Static` should be the same as `T` \ 366 | but with its lifetimes replaced by `'static`" 367 | ); 368 | } 369 | 370 | mod std_impls { 371 | #![allow(unused_parens)] 372 | use super::{Static, Transient}; 373 | 374 | macro_rules! impl_refs { 375 | { 376 | $type_:ty 377 | [$($param:tt $(: $bound1:tt $(+ $bounds:tt)*)?),*] 378 | $( ($($trans:ty),+) )? 379 | } => { 380 | unsafe impl<'_a, $( $param $( : $bound1 $(+ $bounds )* )? ),*> 381 | Transient for &'_a $type_ { 382 | type Static = &'static <$type_ as Transient>::Static; 383 | type Transience = (Co<'_a> $($(, $trans)+)?); 384 | } 385 | 386 | unsafe impl<'_a, $( $param $( : $bound1 $(+ $bounds )* )? ),*> 387 | Transient for &'_a mut $type_ { 388 | type Static = &'static mut <$type_ as Transient>::Static; 389 | type Transience = (Co<'_a> $($(, $trans)+)?); 390 | } 391 | 392 | unsafe impl<'_a, '_b, $( $param $( : $bound1 $(+ $bounds )* )? ),*> 393 | Transient for &'_a &'_b $type_ { 394 | type Static = &'static &'static <$type_ as Transient>::Static; 395 | type Transience = (Co<'_a>, Co<'_b> $($(, $trans)+)?); 396 | } 397 | 398 | unsafe impl<'_a, '_b, $( $param $( : $bound1 $(+ $bounds )* )? ),*> 399 | Transient for &'_a mut &'_b $type_ { 400 | type Static = &'static mut &'static <$type_ as Transient>::Static; 401 | type Transience = (Co<'_a>, Inv<'_b> $($(, $trans)+)?); 402 | } 403 | 404 | unsafe impl<'_a, '_b, $( $param $( : $bound1 $(+ $bounds )* )? ),*> 405 | Transient for &'_a &'_b mut $type_ { 406 | type Static = &'static &'static mut <$type_ as Transient>::Static; 407 | type Transience = (Co<'_a>, Co<'_b> $($(, $trans)+)?); 408 | } 409 | 410 | unsafe impl<'_a, '_b, $( $param $( : $bound1 $(+ $bounds )* )? ),*> 411 | Transient for &'_a mut &'_b mut $type_ { 412 | type Static = &'static mut &'static <$type_ as Transient>::Static; 413 | type Transience = (Co<'_a>, Inv<'_b> $($(, $trans)+)?); 414 | } 415 | } 416 | } 417 | 418 | macro_rules! impl_static { 419 | ( $($ty:ty),* $(,)? ) => { 420 | $( 421 | impl Static for $ty {} 422 | impl_refs!($ty []); 423 | )* 424 | } 425 | } 426 | 427 | macro_rules! impl_fn_pointers { 428 | { $( ($($In:ident),*) ),* } => { 429 | $( 430 | unsafe impl<$($In,)* Out> Transient for fn($($In),*) -> Out 431 | where 432 | $($In: Transient,)* 433 | Out: Transient, 434 | { 435 | type Static = fn($($In::Static),*) -> Out::Static; 436 | type Transience = ($(Contravariant<$In>,)* Covariant); 437 | } 438 | )* 439 | }; 440 | } 441 | 442 | /// impls for types that do not require `std` or `alloc` 443 | mod _core { 444 | use super::{Static, Transient}; 445 | use crate::{Co, Contravariant, Covariant, Inv, Invariant}; 446 | 447 | impl_static! { 448 | isize, i8, i16, i32, i64, i128, 449 | usize, u8, u16, u32, u64, u128, 450 | f32, f64, (), bool, 451 | ::core::char::ParseCharError, 452 | ::core::char::DecodeUtf16Error, 453 | ::core::convert::Infallible, 454 | ::core::num::ParseIntError, 455 | ::core::num::ParseFloatError, 456 | ::core::num::IntErrorKind, 457 | ::core::num::TryFromIntError, 458 | ::core::str::ParseBoolError, 459 | ::core::str::Utf8Error, 460 | ::core::fmt::Error, 461 | } 462 | 463 | // the `net` module was not moved from `std` to `core` until Rust 1.77, so the 464 | // "std" feature is required on earlier versions (see the `_std` submodule) 465 | #[rustversion::since(1.77)] 466 | impl_static!(::core::net::AddrParseError); 467 | 468 | unsafe impl<'a> Transient for &'a str { 469 | type Static = &'static str; 470 | type Transience = Co<'a>; 471 | } 472 | impl_refs!(&'a str ['a]); 473 | 474 | unsafe impl<'a> Transient for &'a mut str { 475 | type Static = &'static mut str; 476 | type Transience = Co<'a>; 477 | } 478 | impl_refs!(&'a mut str ['a]); 479 | 480 | unsafe impl<'a, T: Transient> Transient for &'a [T] { 481 | type Static = &'static [T::Static]; 482 | type Transience = (Co<'a>, Covariant); 483 | } 484 | impl_refs!(&'a [T] ['a, T: Transient] (Co<'a>, Covariant)); 485 | 486 | unsafe impl<'a, T: Transient> Transient for &'a mut [T] { 487 | type Static = &'static mut [T::Static]; 488 | type Transience = (Co<'a>, Invariant); 489 | } 490 | impl_refs!(&'a mut [T] ['a, T: Transient] (Co<'a>, Invariant)); 491 | 492 | unsafe impl Transient for Option { 493 | type Static = Option; 494 | type Transience = Covariant; 495 | } 496 | 497 | unsafe impl Transient for Result { 498 | type Static = Result; 499 | type Transience = Covariant; 500 | } 501 | 502 | unsafe impl Transient for ::core::marker::PhantomData { 503 | type Static = ::core::marker::PhantomData; 504 | type Transience = Covariant; 505 | } 506 | 507 | unsafe impl Transient for ::core::cell::Cell { 508 | type Static = ::core::marker::PhantomData; 509 | type Transience = Invariant; 510 | } 511 | 512 | unsafe impl Transient for *const T { 513 | type Static = *const T::Static; 514 | type Transience = Covariant; 515 | } 516 | 517 | unsafe impl Transient for *mut T { 518 | type Static = *mut T::Static; 519 | type Transience = Invariant; 520 | } 521 | 522 | unsafe impl Transient for core::ptr::NonNull { 523 | type Static = core::ptr::NonNull; 524 | type Transience = Covariant; 525 | } 526 | 527 | unsafe impl<'a> Transient for &'a dyn ::core::any::Any { 528 | type Static = &'static dyn ::core::any::Any; 529 | type Transience = Co<'a>; 530 | } 531 | 532 | unsafe impl<'a> Transient for &'a mut dyn ::core::any::Any { 533 | type Static = &'static mut dyn ::core::any::Any; 534 | type Transience = Co<'a>; 535 | } 536 | 537 | impl_fn_pointers! { 538 | (), (In1), (In1, In2), (In1, In2, In3), (In1, In2, In3, In4) 539 | } 540 | } 541 | 542 | /// impls that require either the `std` or `alloc` feature 543 | #[cfg(feature = "alloc")] 544 | mod _alloc { 545 | use super::{Static, Transient}; 546 | use crate::{Co, Covariant, Inv}; 547 | use alloc::borrow; 548 | use alloc::boxed::Box; 549 | use alloc::collections; 550 | use alloc::string; 551 | use alloc::vec::Vec; 552 | 553 | impl_static! { 554 | Box, 555 | Box, 556 | string::String, 557 | string::FromUtf8Error, 558 | string::FromUtf16Error, 559 | } 560 | 561 | unsafe impl<'a, T> Transient for borrow::Cow<'a, T> 562 | where 563 | T: Transient + borrow::ToOwned, 564 | T::Static: borrow::ToOwned, 565 | { 566 | type Static = borrow::Cow<'static, T::Static>; 567 | type Transience = (Co<'a>, Covariant); 568 | } 569 | 570 | unsafe impl Transient for Vec { 571 | type Static = Vec; 572 | type Transience = Covariant; 573 | } 574 | impl_refs!(Vec [T: Transient] (Covariant)); 575 | 576 | unsafe impl Transient for Box<[T]> { 577 | type Static = Box<[T::Static]>; 578 | type Transience = Covariant; 579 | } 580 | 581 | unsafe impl Transient for collections::BTreeMap { 582 | type Static = collections::BTreeMap; 583 | type Transience = (Covariant, Covariant); 584 | } 585 | impl_refs!( 586 | collections::BTreeMap 587 | [K: Transient, V: Transient] 588 | (Covariant, Covariant) 589 | ); 590 | 591 | unsafe impl Transient for collections::BTreeSet { 592 | type Static = collections::BTreeSet; 593 | type Transience = Covariant; 594 | } 595 | impl_refs!( 596 | collections::BTreeSet 597 | [T: Transient] 598 | (Covariant) 599 | ); 600 | 601 | unsafe impl Transient for collections::LinkedList { 602 | type Static = collections::LinkedList; 603 | type Transience = Covariant; 604 | } 605 | impl_refs!( 606 | collections::LinkedList 607 | [T: Transient] 608 | (Covariant) 609 | ); 610 | 611 | unsafe impl Transient for collections::VecDeque { 612 | type Static = collections::VecDeque; 613 | type Transience = Covariant; 614 | } 615 | impl_refs!( 616 | collections::VecDeque 617 | [T: Transient] 618 | (Covariant) 619 | ); 620 | } 621 | 622 | /// impls that require the `std` feature 623 | #[cfg(feature = "std")] 624 | mod _std { 625 | use super::{Static, Transient}; 626 | use crate::{Co, Covariant, Inv}; 627 | use std::collections::HashMap; 628 | 629 | impl_static! { 630 | std::io::Error, 631 | std::io::ErrorKind, 632 | std::env::VarError, 633 | std::env::JoinPathsError, 634 | std::time::SystemTimeError, 635 | } 636 | 637 | unsafe impl Transient for HashMap { 638 | type Static = HashMap; 639 | type Transience = (Covariant, Covariant); 640 | } 641 | impl_refs!( 642 | HashMap 643 | [K: Transient, V: Transient] 644 | (Covariant, Covariant) 645 | ); 646 | 647 | // on later Rust versions this impl is available without the "std" feature 648 | #[rustversion::before(1.77)] 649 | impl_static!(::std::net::AddrParseError); 650 | } 651 | } 652 | 653 | #[cfg(feature = "ndarray")] 654 | mod ndarray_impls { 655 | use ndarray::{ArcArray, Array, ArrayView, ArrayViewMut, CowArray, Dimension}; 656 | 657 | /// Requires the `ndarray` crate feature 658 | impl crate::Static for Array {} 659 | 660 | /// Requires the `ndarray` crate feature 661 | unsafe impl<'a, T, D> crate::Transient for ArrayView<'a, T, D> 662 | where 663 | T: 'static, 664 | D: Dimension + 'static, 665 | { 666 | type Static = ArrayView<'static, T, D>; 667 | type Transience = crate::Co<'a>; 668 | } 669 | 670 | /// Requires the `ndarray` crate feature 671 | unsafe impl<'a, T, D> crate::Transient for ArrayViewMut<'a, T, D> 672 | where 673 | T: 'static, 674 | D: Dimension + 'static, 675 | { 676 | type Static = ArrayViewMut<'static, T, D>; 677 | type Transience = crate::Co<'a>; 678 | } 679 | 680 | /// Requires the `ndarray` crate feature 681 | unsafe impl crate::Transient for ArcArray 682 | where 683 | T: 'static, 684 | D: Dimension + 'static, 685 | { 686 | type Static = ArcArray; 687 | type Transience = (); 688 | } 689 | 690 | /// Requires the `ndarray` crate feature 691 | unsafe impl<'a, T, D> crate::Transient for CowArray<'a, T, D> 692 | where 693 | T: 'static, 694 | D: Dimension + 'static, 695 | { 696 | type Static = CowArray<'static, T, D>; 697 | type Transience = crate::Co<'a>; 698 | } 699 | } 700 | 701 | #[cfg(feature = "pyo3")] 702 | mod pyo3_impls { 703 | use crate::{tr::Transient, Co, Static}; 704 | use pyo3::pyclass::{boolean_struct::False, PyClass}; 705 | use pyo3::{Borrowed, Bound, Py, PyErr, PyRef, PyRefMut}; 706 | 707 | /// Requires the `pyo3` crate feature 708 | impl Static for Py {} 709 | 710 | /// Requires the `pyo3` crate feature 711 | unsafe impl<'py, T: 'static> Transient for Bound<'py, T> { 712 | type Static = Bound<'static, T>; 713 | type Transience = Co<'py>; 714 | } 715 | 716 | /// Requires the `pyo3` crate feature 717 | unsafe impl<'a, 'py, T: 'static> Transient for Borrowed<'a, 'py, T> { 718 | type Static = Borrowed<'static, 'static, T>; 719 | type Transience = (Co<'a>, Co<'py>); 720 | } 721 | 722 | /// Requires the `pyo3` crate feature 723 | unsafe impl<'py, T: PyClass> Transient for PyRef<'py, T> { 724 | type Static = PyRef<'static, T>; 725 | type Transience = Co<'py>; 726 | } 727 | 728 | /// Requires the `pyo3` crate feature 729 | unsafe impl<'py, T: PyClass> Transient for PyRefMut<'py, T> { 730 | type Static = PyRefMut<'static, T>; 731 | type Transience = Co<'py>; 732 | } 733 | 734 | /// Requires the `pyo3` crate feature 735 | impl Static for PyErr {} 736 | } 737 | 738 | #[cfg(feature = "numpy")] 739 | mod numpy_impls { 740 | use numpy::ndarray::Dimension; 741 | use numpy::{Element, PyReadonlyArray, PyReadwriteArray}; 742 | 743 | /// Requires the `numpy` crate feature 744 | unsafe impl<'py, T, D> crate::Transient for PyReadonlyArray<'py, T, D> 745 | where 746 | T: Element + 'static, 747 | D: Dimension + 'static, 748 | { 749 | type Static = PyReadonlyArray<'static, T, D>; 750 | type Transience = crate::Co<'py>; 751 | } 752 | 753 | /// Requires the `numpy` crate feature 754 | unsafe impl<'py, T, D> crate::Transient for PyReadwriteArray<'py, T, D> 755 | where 756 | T: Element + 'static, 757 | D: Dimension + 'static, 758 | { 759 | type Static = PyReadwriteArray<'static, T, D>; 760 | type Transience = crate::Co<'py>; 761 | } 762 | } 763 | 764 | #[cfg(feature = "uuid")] 765 | mod uuid_impls { 766 | use super::Static; 767 | use uuid::*; 768 | impl Static for Uuid {} 769 | impl Static for Error {} 770 | impl Static for Variant {} 771 | impl Static for Version {} 772 | } 773 | 774 | #[cfg(feature = "either")] 775 | mod either_impls { 776 | use crate::Covariant; 777 | use either::Either; 778 | 779 | unsafe impl crate::Transient for Either 780 | where 781 | L: crate::Transient, 782 | R: crate::Transient, 783 | { 784 | type Static = Either; 785 | type Transience = (Covariant, Covariant); 786 | } 787 | } 788 | 789 | #[cfg(feature = "serde_json")] 790 | mod serde_json_impls { 791 | use serde_json::Error; 792 | 793 | impl crate::Static for Error {} 794 | } 795 | 796 | #[cfg(feature = "rmp-serde")] 797 | mod rmp_serde_impls { 798 | use rmp_serde::{decode, encode}; 799 | 800 | impl crate::Static for encode::Error {} 801 | impl crate::Static for decode::Error {} 802 | } 803 | -------------------------------------------------------------------------------- /src/transience.rs: -------------------------------------------------------------------------------- 1 | //! Defines the [`Transience`] trait as well as the [`Inv`], [`Co`], and [`Contra`] 2 | //! structs that implement it. This module also defines the [`CanTranscendTo`] and 3 | //! [`CanRecoverFrom`] traits that establish the allowable transitions between 4 | //! transiences. 5 | use crate::tr::Transient; 6 | use core::marker::PhantomData; 7 | 8 | /// Marker trait for types used to establish the [variance] of a type with 9 | /// respect to each of its lifetime parameters, including [`Co`], [`Contra`], 10 | /// [`Inv`], [`Timeless`], and tuples combining them. 11 | /// 12 | /// Note that even though most types are *covariant* in reality, this crate 13 | /// treats *invariance* as the default since any other assumption could cause 14 | /// undefined behavior if chosen incorrectly. To override this default, the 15 | /// [`Transience` associated type] can be set in the type's `Transient` 16 | /// implementation; if using the derive macro, this corresponds to including 17 | /// the `#[covariant]` or `#[contravariant]` attribute. 18 | /// 19 | /// To maximize the flexibility of this crate's functionality, transitions 20 | /// between compatible `Transiences` are supported. See the documentation for 21 | /// the [`CanTranscendTo`] and[ `CanRecoverFrom`] traits for an introduction 22 | /// to these transitions and when they are used, as well as a discussion of 23 | /// why certain transitions are allowed while others are forbidden. 24 | /// 25 | /// ## Valid transitions table 26 | /// The following table summarizes the allowable transitions that can be 27 | /// made between (single-lifetime) transiences, where the _rows_ represent 28 | /// the starting point for a type (i.e. the `Transience` it defines in its 29 | /// `Transient` impl), and the _columns_ represent a transience it wishes to 30 | /// transition to (i.e. when being cast to `dyn Any<_>`). Upon being 31 | /// [`downcast`] the trait object would then return to a _row_ in the table, 32 | /// ideally back where it started (although not always, as discussed in the 33 | /// next section): 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 | ///
Inv<'short> Inv<'long> Co<'short> Co<'long> Contra<'short> Contra<'long> Timeless
Inv<'short> yes no no no no no no
Inv<'long> no yes no no no no no
Co<'short> yes no yes no no no no
Co<'long> yes yes yes yes no no no
Contra<'short> yes yes no no yes yes no
Contra<'long> no yes no no no yes no
Timeless yes yes yes yes yes yes yes
117 | /// 118 | /// ## Lossy transitions 119 | /// The yellow transitions marked with an asterisk in the above table are allowed, 120 | /// but lossy; the recovered lifetime will be reduced (`Co`) or increased (`Contra`). 121 | /// For example, a type `&'long i32` with a transience of `Co<'long>` (middle row) 122 | /// can be safely erased to the trait object `dyn Any>` (3rd column) by 123 | /// shortening its lifetime. However, when we want to 124 | /// [`downcast`] the opaque trait object back into its concrete type, we _cannot_ 125 | /// assume that we can safely recover 126 | /// a `'long` lifetime from the `'short` lifetime of the trait object. The only truly 127 | /// safe solution is to avoid downcasting to a shorter lifetime - just as if you were 128 | /// working with references to the type itself. 129 | /// 130 | /// # Safety 131 | /// The `Inverse` associated types must be defined appropriately to reflect the 132 | /// _opposite_ variance behavior with respect to the same lifetime parameter(s). 133 | /// Similarly, the `Invariant` associated type must be defined as an _invariant_ 134 | /// behavior with respect to the same lifetime parameter(s). Note the this does 135 | /// _not_ apply to the `Timeless` type, which has no lifetime relationships and 136 | /// may thus use `Timeless` for both associated types. 137 | /// 138 | /// [`Transience` associated type]: Transient::Transience 139 | /// [variance]: https://doc.rust-lang.org/nomicon/subtyping.html 140 | /// [`downcast`]: crate::Downcast::downcast 141 | pub unsafe trait Transience: 142 | Transient + CanTranscendTo + CanRecoverFrom 143 | { 144 | /// The inverse of this transience. `Co` <=> `Contra`, and `Inv` <=> `Inv`. 145 | type Inverse: Transience; 146 | /// The invariant version of this transience. `Co`, `Contra` => `Inv`. 147 | type Invariant: Transience; 148 | } 149 | 150 | /// Unsafe marker trait indicating that the implementing [`Transience`] can 151 | /// safely upcast to the parameterizing `Transience`. 152 | /// 153 | /// When `R: Transience` implements `CanTranscendTo`, it is making an 154 | /// `unsafe` assertion that it is a [subtype] of `Other` and can safely be used 155 | /// anywhere that `Other` is expected. In the context of the `transient` crate, 156 | /// this implementation empowers a type [`T: Transient`] to be 157 | /// erased to [`dyn Any`] in addition the `dyn Any` suggested by its 158 | /// `Transient` impl. This would result in undefined behavior if `R` was not 159 | /// actually a subtype of `Other`, hence the `unsafe` declaration on this trait. 160 | /// 161 | /// This trait is closely related to the [`CanRecoverFrom`] trait which bounds 162 | /// the transiences that can be recovered when restored `dyn Any` to its 163 | /// concrete type `T`. 164 | /// 165 | /// The foundational set of valid transitions (which correspond to implementations 166 | /// of this trait) can be summarized as: 167 | /// 168 | /// - `Timeless` --> any `R: Transience` 169 | /// - `Co<'long>` --> `Co<'short>` 170 | /// - `Co<'long>` --> `Inv<'short>` 171 | /// - `Contra<'short>` --> `Contra<'long>` 172 | /// - `Contra<'short>` --> `Inv<'long>` 173 | /// - any `R: Transience` --> Self 174 | /// 175 | /// Additionally, this trait is _composable_ such that a tuple of transiences 176 | /// can safely implement it for another equal-length tuple when each of its 177 | /// components implement the trait for the corresponding component in the 178 | /// target tuple (i.e. `(R1, R2): CanTranscendTo<(S1, S2)>` iff 179 | /// `R1: CanTranscendTo` and `R2: CanTranscendTo`. These transitions 180 | /// are not exhaustively implemented, but can be expanded as needs arise. 181 | /// 182 | /// Finally, some convenience implementations such as `R` --> `(R,)` and 183 | /// `(R,)` --> `R` are provided. 184 | /// 185 | /// # Safety 186 | /// This trait must only be implemented for *valid* conversions in accordance 187 | /// with [subtype] relationships as discussed above. Implementing this trait 188 | /// for an *invalid* conversion (such as shortening the lifetime of a 189 | /// *contravariant* type) can lead to undefined behavior. 190 | /// 191 | /// [`T: Transient`]: crate::Transient 192 | /// [`dyn Any`]: crate::Any 193 | /// [subtype]: https://doc.rust-lang.org/nomicon/subtyping.html 194 | pub unsafe trait CanTranscendTo {} 195 | 196 | /// Unsafe marker trait indicating that the implementing [`Transience`] can be 197 | /// safely recovered from the parameterizing `Transience`. 198 | /// 199 | /// When `R: Transience` implements `CanRecoverFrom`, it empowers a type 200 | /// [`T: Transient`] to be "recovered from" [`dyn Any`] 201 | /// when downcasting using the [`Downcast::downcast::`] and similar methods. 202 | /// Allowing this operation when not appropriate, such as allowing `&'long i32` 203 | /// (which implements `Transient>`) to be recovered from 204 | /// a `dyn Any>` which may have started as `&'short i32`, could 205 | /// easily lead to undefined behavior. 206 | /// 207 | /// This trait is *almost* equivalent to the [`CanTranscendTo`] trait from the 208 | /// opposite perspective, but with a few exceptions to allow more flexible 209 | /// recovery. For example, we might erase a type `T: Transient>` 210 | /// to `dyn Any>` so that we can store it with invariant types, and this is 211 | /// allowed because `Co<'a>: CanTranscendTo>` (i.e. `Co<'a>` is a [subtype] 212 | /// of `Inv<'a>`) . However, downcasting the erased type back to `T` would require 213 | /// reverting `Inv<'a>` to `Co<'a>`, which is *not* a valid conversion according 214 | /// to the subtyping relationships as modeled by the `CanTranscendTo` trait. While 215 | /// it is true that converting `Inv<'a>` to `Co<'a>` would not be sound in general, 216 | /// it is fine to do this while downcasting because the trait abject is not kept 217 | /// around where the altered transience could be abused and lead to UB. 218 | /// 219 | /// The impls provided are very similar to `CanTranscendTo`, except: 220 | /// - `Co` can be recovered from `Inv` at the same (or shorter) lifetime. 221 | /// - `Contra` can be recovered from `Inv` at the same (or longer) lifetime. 222 | /// - `Timeless` can be recovered from *any* transience. 223 | /// - The only transience that can be recovered from `Timeless` is itself. This 224 | /// is only for technical reasons (to avoid duplicate impls), and in practice it 225 | /// doesn't sacrifice flexibility because the only safe way to obtain `dyn Any<())>` 226 | /// in the first place is if the original type was `T: Transient`. 227 | /// 228 | /// # Safety 229 | /// This trait must only be implemented for *valid* conversions. Implementing 230 | /// this trait for an *invalid* conversion (such as shortening the lifetime 231 | /// of a *contravariant* type) can lead to undefined behavior. 232 | /// 233 | /// [`Downcast::downcast::`]: crate::Downcast::downcast 234 | /// [subtype]: https://doc.rust-lang.org/nomicon/subtyping.html 235 | pub unsafe trait CanRecoverFrom {} 236 | 237 | /// Used as the `Transience` of a type to declare that it is `'static` and not 238 | /// dependent on any lifetime parameters. 239 | /// 240 | /// Such types only contain owned data and static references, and are thus much 241 | /// safer to work with and allow several restrictions imposed by the crate to 242 | /// be loosened. The [`transient::Any`][crate::Any] trait is parameterized with 243 | /// this transience by default so that it can mimic the simplicity of the 244 | /// [`std::any::Any`] trait in the simple case of `'static` types. 245 | pub type Timeless = (); 246 | 247 | unsafe impl Transience for Timeless { 248 | type Inverse = (); 249 | type Invariant = (); 250 | } 251 | 252 | /// Used to declare an [_invariant_] relationship between a type and its lifetime 253 | /// parameter. 254 | /// 255 | /// An [_invariant_] type is one for which the compiler cannot safely assume that 256 | /// its lifetime may be shortened *or* lengthened (e.g. `'b` in `&'a mut &'b T`). 257 | /// Such a type must therefore match the expected lifetime exactly when passed to 258 | /// a function. 259 | /// 260 | /// See the [`Transience`] documentation for more information. 261 | /// 262 | /// [_invariant_]: https://doc.rust-lang.org/nomicon/subtyping.html 263 | #[derive(Clone, Copy, Debug)] 264 | pub struct Inv<'a>(PhantomData &'a ()>); 265 | 266 | unsafe impl<'a> Transience for Inv<'a> { 267 | type Inverse = Inv<'a>; 268 | type Invariant = Inv<'a>; 269 | } 270 | 271 | unsafe impl<'a> Transient for Inv<'a> { 272 | type Static = Inv<'static>; 273 | type Transience = Self; 274 | } 275 | 276 | /// Used to declare a [_covariant_] relationship between a type and its lifetime 277 | /// parameter. 278 | /// 279 | /// A [_covariant_] type is one for which the compiler can safely *shorten* its 280 | /// lifetime parameter as needed when passing it to a function; for example, 281 | /// `&'a T` is *covariant* w.r.t. `'a`, so `&'long str` can be used where 282 | /// `&'short str` is expected. 283 | /// 284 | /// See the [`Transience`] documentation for more information. 285 | /// 286 | /// [_covariant_]: https://doc.rust-lang.org/nomicon/subtyping.html 287 | #[derive(Clone, Copy, Debug)] 288 | pub struct Co<'a>(PhantomData<&'a ()>); 289 | 290 | unsafe impl<'a> Transience for Co<'a> { 291 | type Inverse = Contra<'a>; 292 | type Invariant = Inv<'a>; 293 | } 294 | 295 | unsafe impl<'a> Transient for Co<'a> { 296 | type Static = Co<'static>; 297 | type Transience = Self; 298 | } 299 | 300 | /// Used to declare a [_contravariant_] relationship between a type and its lifetime 301 | /// parameter. 302 | /// 303 | /// A [_contravariant_] type is one for which the compiler can safely *lengthen* 304 | /// its lifetime parameter as needed when passing it to a function; for example, 305 | /// `fn(&'a str)` is *contravariant* w.r.t. `'a`, so `fn(&'short str)` can be 306 | /// used where `fn(&'long str)` is expected. 307 | /// 308 | /// See the [`Transience`] documentation for more information. 309 | /// 310 | /// [_contravariant_]: https://doc.rust-lang.org/nomicon/subtyping.html 311 | #[derive(Clone, Copy, Debug)] 312 | pub struct Contra<'a>(PhantomData); 313 | 314 | unsafe impl<'a> Transience for Contra<'a> { 315 | type Inverse = Co<'a>; 316 | type Invariant = Inv<'a>; 317 | } 318 | 319 | unsafe impl<'a> Transient for Contra<'a> { 320 | type Static = Contra<'static>; 321 | type Transience = Self; 322 | } 323 | 324 | /// Type alias that can be used in the [`Transience`] of a type to declare that it is 325 | /// [_covariant_] with respect to a [`Transient`] type parameter. 326 | /// 327 | /// This is analogous to the [`Co<'a>`][Co] type that is used to declare a covariant 328 | /// relationship with respect to a lifetime parameter. 329 | /// 330 | /// As discussed in its safety docs, the [`Transient`] trait requires an implementing 331 | /// type to declare its temporal relationships in the [`Transience` associated type]. 332 | /// This most naturally refers to its variance with respect to its _lifetime_ parameters, 333 | /// but it is _critical_ to account for that of its _type_ parameters as well when they 334 | /// are allowed to be non-`'static`. This type, as well as the related [`Invariant`] and 335 | /// [`Contravariant`] types, may be used to declare the variance of this relationship 336 | /// by using it, or a tuple including it, as the `Transience`. 337 | /// 338 | /// # Example 339 | /// Consider the following type, which is _covariant_ with respect to both the 340 | /// lifetime parameter `'a` and the type parameter `T`: 341 | /// ``` 342 | /// struct S<'a, T>(&'a [T]); 343 | /// ``` 344 | /// 345 | /// If we wanted to provide a `Transient` implementation for this type that allows 346 | /// the type parameter to be non-`'static`, we can use this `Covariant` type 347 | /// alias as a component in the `Transience` associated type: 348 | /// ``` 349 | /// # struct S<'a, T>(&'a [T]); 350 | /// use transient::{Transient, Co, Covariant}; 351 | /// 352 | /// unsafe impl<'a, T> Transient for S<'a, T> 353 | /// where 354 | /// T: Transient 355 | /// { 356 | /// type Static = S<'static, T::Static>; 357 | /// type Transience = (Co<'a>, Covariant); 358 | /// } 359 | /// ``` 360 | /// 361 | /// When a concrete type of `&'b i32` is substituted for `T`, this impl expands to 362 | /// the following which properly accounts for the lifetime `'a`, as well as the 363 | /// lifetime `'b` that was hidden inside of the generic parameter `T`: 364 | /// ``` 365 | /// # struct S<'a, T>(&'a [T]); 366 | /// # use transient::{Transient, Co, Covariant}; 367 | /// unsafe impl<'a, 'b> Transient for S<'a, &'b i32> { 368 | /// type Static = S<'static, &'static i32>; 369 | /// type Transience = (Co<'a>, Co<'b>); 370 | /// } 371 | /// ``` 372 | /// 373 | /// [`Static` associated type]: Transient::Transience 374 | /// [`Transience` associated type]: Transient::Transience 375 | /// [_covariant_]: https://doc.rust-lang.org/nomicon/subtyping.html 376 | pub type Covariant = ::Transience; 377 | 378 | /// Type alias that can be used in the [`Transience`] of a type to declare that it is 379 | /// [_contravariant_] with respect to a [`Transient`] type parameter. 380 | /// 381 | /// This is analogous to the [`Contra<'a>`][Contra] type that is used to declare a 382 | /// contravariant relationship with respect to a lifetime parameter. 383 | /// 384 | /// See the [`Covariant`] type for more information, including a usage example. 385 | /// 386 | /// [_contravariant_]: https://doc.rust-lang.org/nomicon/subtyping.html 387 | pub type Contravariant = <::Transience as Transience>::Inverse; 388 | 389 | /// Type alias that can be used in the [`Transience`] of a type to declare that it is 390 | /// [_invariant_] with respect to a [`Transient`] type parameter. 391 | /// 392 | /// This is analogous to the [`Inv<'a>`][Inv] type that is used to declare an invariant 393 | /// relationship with respect to a lifetime parameter. 394 | /// 395 | /// See the [`Covariant`] type for more information, including a usage example. 396 | /// 397 | /// [_invariant_]: https://doc.rust-lang.org/nomicon/subtyping.html 398 | pub type Invariant = <::Transience as Transience>::Invariant; 399 | 400 | /// Type alias that can be nested within the [`Covariant`], [`Contravariant`], or 401 | /// [`Invariant`] type to declare the [_variance_] of a [`Transient`] struct with 402 | /// respect to a lifetime parameter. 403 | /// 404 | /// Using this type is purely for aesthetics, and reduces to one of the fundamental 405 | /// [`Transience`] types: 406 | /// 407 | /// - `Covariant>` is equivalent to [`Co<'a>`] 408 | /// - `Contravariant>` is equivalent to [`Contra<'a>`] 409 | /// - `Invariant>` is equivalent to [`Inv<'a>`] 410 | /// 411 | /// Using `Co`, `Contra`, or `Inv` directly should usually be preferred, but some users 412 | /// may find that this type increases clarity when use alongside type parameter variances 413 | /// such as in the `Transience` tuple `(Covariant>, Covariant>)` 414 | /// 415 | /// [_variance_]: https://doc.rust-lang.org/nomicon/subtyping.html 416 | pub type Lifetime<'a> = PhantomData<&'a ()>; 417 | 418 | // ************************************************************************* // 419 | // ************************* SAFETY-CRITICAL LOGIC! ************************ // 420 | // ************************************************************************* // 421 | // The following impls define the allowable transitions between variances, // 422 | // amd play key roles in upholding safety guarantees. All other impls are // 423 | // derived from these rules, so it is critical that they be correct. // 424 | // ************************************************************************* // 425 | 426 | // This has to be individual impls, to avoid conflicting with the blanket 427 | // impls below 428 | unsafe impl CanTranscendTo for Timeless {} 429 | unsafe impl CanRecoverFrom for Timeless {} 430 | unsafe impl<'a> CanTranscendTo> for Timeless {} 431 | unsafe impl<'a> CanRecoverFrom> for Timeless {} 432 | unsafe impl<'a> CanTranscendTo> for Timeless {} 433 | unsafe impl<'a> CanRecoverFrom> for Timeless {} 434 | unsafe impl<'a> CanTranscendTo> for Timeless {} 435 | unsafe impl<'a> CanRecoverFrom> for Timeless {} 436 | 437 | unsafe impl<'a> CanTranscendTo> for Inv<'a> {} 438 | unsafe impl<'a> CanRecoverFrom> for Inv<'a> {} 439 | 440 | unsafe impl<'a, 'b: 'a> CanTranscendTo> for Co<'b> {} 441 | unsafe impl<'a, 'b: 'a> CanRecoverFrom> for Co<'a> {} 442 | 443 | unsafe impl<'a, 'b: 'a> CanTranscendTo> for Co<'b> {} 444 | unsafe impl<'a, 'b: 'a> CanRecoverFrom> for Inv<'a> {} 445 | 446 | unsafe impl<'a, 'b: 'a> CanTranscendTo> for Contra<'a> {} 447 | unsafe impl<'a, 'b: 'a> CanRecoverFrom> for Contra<'b> {} 448 | 449 | unsafe impl<'a, 'b: 'a> CanTranscendTo> for Contra<'a> {} 450 | unsafe impl<'a, 'b: 'a> CanRecoverFrom> for Inv<'b> {} 451 | 452 | unsafe impl<'a> CanRecoverFrom> for Co<'a> {} 453 | unsafe impl<'a> CanRecoverFrom> for Contra<'a> {} 454 | 455 | // ************************************************************** // 456 | 457 | /// Private macro implementing the transitions between each scalar 458 | /// transience and a 1-, 2-, or 3-tuple of compatible transiences. 459 | /// This is necessary because blanket impls would overlap. 460 | macro_rules! impl_scalar_to_tuples { 461 | ( $($typ:ty),* ) => { 462 | $( 463 | // scalar => 1-tuple* => scalar 464 | unsafe impl<'a, R> CanTranscendTo<(R,)> for $typ 465 | where $typ: CanTranscendTo {} 466 | unsafe impl<'a, R> CanRecoverFrom<(R,)> for $typ 467 | where $typ: CanRecoverFrom {} 468 | 469 | // scalar => 2-tuple* => scalar 470 | unsafe impl<'a, R1, R2> CanTranscendTo<(R1, R2,)> for $typ 471 | where $typ: CanTranscendTo + CanTranscendTo {} 472 | unsafe impl<'a, R1, R2> CanRecoverFrom<(R1, R2,)> for $typ 473 | where $typ: CanRecoverFrom + CanRecoverFrom {} 474 | 475 | // scalar => 3-tuple* => scalar 476 | unsafe impl<'a, R1, R2, R3> CanTranscendTo<(R1, R2, R3,)> for $typ 477 | where $typ: CanTranscendTo + CanTranscendTo + CanTranscendTo {} 478 | unsafe impl<'a, R1, R2, R3> CanRecoverFrom<(R1, R2, R3,)> for $typ 479 | where $typ: CanRecoverFrom + CanRecoverFrom + CanRecoverFrom {} 480 | 481 | // scalar => 4-tuple* => scalar 482 | unsafe impl<'a, R1, R2, R3, R4> CanTranscendTo<(R1, R2, R3, R4)> for $typ 483 | where $typ: CanTranscendTo + CanTranscendTo 484 | + CanTranscendTo + CanTranscendTo {} 485 | unsafe impl<'a, R1, R2, R3, R4> CanRecoverFrom<(R1, R2, R3, R4)> for $typ 486 | where $typ: CanRecoverFrom + CanRecoverFrom 487 | + CanRecoverFrom + CanRecoverFrom {} 488 | 489 | // scalar => 5-tuple* => scalar 490 | unsafe impl<'a, R1, R2, R3, R4, R5> CanTranscendTo<(R1, R2, R3, R4, R5)> for $typ 491 | where $typ: CanTranscendTo + CanTranscendTo 492 | + CanTranscendTo + CanTranscendTo 493 | + CanTranscendTo {} 494 | unsafe impl<'a, R1, R2, R3, R4, R5> CanRecoverFrom<(R1, R2, R3, R4, R5)> for $typ 495 | where $typ: CanRecoverFrom + CanRecoverFrom 496 | + CanRecoverFrom + CanRecoverFrom 497 | + CanRecoverFrom{} 498 | 499 | // ------------------------------------------ 500 | 501 | // 1-tuple* => scalar => 1-tuple* 502 | unsafe impl<'a, R,> CanTranscendTo<$typ> for (R,) 503 | where R: CanTranscendTo<$typ> {} 504 | unsafe impl<'a, R,> CanRecoverFrom<$typ> for (R,) 505 | where R: CanRecoverFrom<$typ> {} 506 | 507 | // 2-tuple* => scalar => 2-tuple* 508 | unsafe impl<'a, R1, R2> CanTranscendTo<$typ> for (R1, R2) 509 | where R1: CanTranscendTo<$typ>, R2: CanTranscendTo<$typ> {} 510 | unsafe impl<'a, R1, R2> CanRecoverFrom<$typ> for (R1, R2) 511 | where R1: CanRecoverFrom<$typ>, R2: CanRecoverFrom<$typ> {} 512 | 513 | // 3-tuple* => scalar => 3-tuple* 514 | unsafe impl<'a, R1, R2, R3> CanTranscendTo<$typ> for (R1, R2, R3) 515 | where R1: CanTranscendTo<$typ>, 516 | R2: CanTranscendTo<$typ>, 517 | R3: CanTranscendTo<$typ>, {} 518 | unsafe impl<'a, R1, R2, R3> CanRecoverFrom<$typ> for (R1, R2, R3) 519 | where R1: CanRecoverFrom<$typ>, 520 | R2: CanRecoverFrom<$typ>, 521 | R3: CanRecoverFrom<$typ>, {} 522 | 523 | // 4-tuple* => scalar => 4-tuple* 524 | unsafe impl<'a, R1, R2, R3, R4> CanTranscendTo<$typ> for (R1, R2, R3, R4) 525 | where R1: CanTranscendTo<$typ>, 526 | R2: CanTranscendTo<$typ>, 527 | R3: CanTranscendTo<$typ>, 528 | R4: CanTranscendTo<$typ>, {} 529 | unsafe impl<'a, R1, R2, R3, R4> CanRecoverFrom<$typ> for (R1, R2, R3, R4) 530 | where R1: CanRecoverFrom<$typ>, 531 | R2: CanRecoverFrom<$typ>, 532 | R3: CanRecoverFrom<$typ>, 533 | R4: CanRecoverFrom<$typ>, {} 534 | 535 | // 5-tuple* => scalar => 5-tuple* 536 | unsafe impl<'a, R1, R2, R3, R4, R5> CanTranscendTo<$typ> for (R1, R2, R3, R4, R5) 537 | where R1: CanTranscendTo<$typ>, 538 | R2: CanTranscendTo<$typ>, 539 | R3: CanTranscendTo<$typ>, 540 | R4: CanTranscendTo<$typ>, 541 | R5: CanTranscendTo<$typ>, {} 542 | unsafe impl<'a, R1, R2, R3, R4, R5> CanRecoverFrom<$typ> for (R1, R2, R3, R4, R5) 543 | where R1: CanRecoverFrom<$typ>, 544 | R2: CanRecoverFrom<$typ>, 545 | R3: CanRecoverFrom<$typ>, 546 | R4: CanRecoverFrom<$typ>, 547 | R5: CanRecoverFrom<$typ>, {} 548 | )* 549 | } 550 | } 551 | impl_scalar_to_tuples! { 552 | Co<'a>, Contra<'a>, Inv<'a>, Timeless 553 | } 554 | 555 | /// implements transitions between equal-length tuples where each sub-transition is 556 | /// also implemented (e.g., `(Co<'long>, Co<'short>)` -> `(Co<'short>, Inv<'short>)`) 557 | macro_rules! impl_equal_tuples { 558 | { $( ($($src:ident,)*) => ($($dst:ident,)*) );* $(;)? } => { 559 | $( 560 | unsafe impl<$($src),*> Transience for ($($src),*,) 561 | where 562 | $( $src: Transience ),* 563 | { 564 | type Inverse = ($(<$src as Transience>::Inverse),*,); 565 | type Invariant = ($(<$src as Transience>::Invariant),*,); 566 | } 567 | unsafe impl<$($src),*> Transient for ($($src),*,) 568 | where 569 | $( $src: Transient ),* 570 | { 571 | type Static = ($(<$src as Transient>::Static),*,); 572 | type Transience = ($(<$src as Transient>::Transience),*,); 573 | } 574 | unsafe impl<$($src),*, $($dst),*> CanTranscendTo<($($dst),*,)> for ($($src),*,) 575 | where 576 | $( $src: CanTranscendTo<$dst> ),* , 577 | $( $dst: Transience ),* , 578 | {} 579 | unsafe impl<$($src),*, $($dst),*> CanRecoverFrom<($($dst),*,)> for ($($src),*,) 580 | where 581 | $( $src: CanRecoverFrom<$dst> ),* 582 | {} 583 | )* 584 | } 585 | } 586 | impl_equal_tuples! { 587 | (A1,) => (A2,); 588 | (A1, B1,) => (A2, B2,); 589 | (A1, B1, C1,) => (A2, B2, C2,); 590 | (A1, B1, C1, D1,) => (A2, B2, C2, D2,); 591 | (A1, B1, C1, D1, E1,) => (A2, B2, C2, D2, E2,); 592 | } 593 | 594 | #[cfg(test)] 595 | mod tests { 596 | use crate::Static; 597 | 598 | use super::*; 599 | 600 | fn assert_can_transend_to() 601 | where 602 | T: Transient, 603 | T::Transience: CanTranscendTo, 604 | { 605 | } 606 | 607 | #[test] 608 | fn validate_static() { 609 | struct A; 610 | impl Static for A {} 611 | struct B; 612 | impl Static for B {} 613 | assert_can_transend_to::<(A, B), ()>(); 614 | } 615 | 616 | #[test] 617 | fn validate_co<'a>() { 618 | #[allow(dead_code)] 619 | struct A<'a>(&'a ()); 620 | unsafe impl<'a> Transient for A<'a> { 621 | type Static = A<'static>; 622 | type Transience = Co<'a>; 623 | } 624 | struct B; 625 | impl Static for B {} 626 | assert_can_transend_to::<(A, B), Co<'a>>(); 627 | #[allow(dead_code)] 628 | struct C<'a>(&'a ()); 629 | unsafe impl<'a> Transient for C<'a> { 630 | type Static = C<'static>; 631 | type Transience = Co<'a>; 632 | } 633 | assert_can_transend_to::<(A<'a>, C), Co<'a>>(); 634 | } 635 | 636 | #[test] 637 | fn validate_contra<'a>() { 638 | #[allow(dead_code)] 639 | struct A<'a>(fn(&'a ())); 640 | unsafe impl<'a> Transient for A<'a> { 641 | type Static = A<'static>; 642 | type Transience = Contra<'a>; 643 | } 644 | struct B; 645 | impl Static for B {} 646 | assert_can_transend_to::<(A<'a>, B), Contra<'a>>(); 647 | #[allow(dead_code)] 648 | struct C<'a>(fn(&'a ())); 649 | unsafe impl<'a> Transient for C<'a> { 650 | type Static = C<'static>; 651 | type Transience = Contra<'a>; 652 | } 653 | assert_can_transend_to::<(A<'a>, C<'a>), Contra<'a>>(); 654 | } 655 | 656 | #[test] 657 | fn validate_inv<'a>() { 658 | #[allow(dead_code)] 659 | struct A<'a>(fn(&'a ()) -> &'a ()); 660 | unsafe impl<'a> Transient for A<'a> { 661 | type Static = A<'static>; 662 | type Transience = Inv<'a>; 663 | } 664 | struct B; 665 | impl Static for B {} 666 | assert_can_transend_to::<(A<'a>, B), Inv<'a>>(); 667 | #[allow(dead_code)] 668 | struct C<'a>(fn(&'a ()) -> &'a ()); 669 | unsafe impl<'a> Transient for C<'a> { 670 | type Static = C<'static>; 671 | type Transience = Inv<'a>; 672 | } 673 | assert_can_transend_to::<(A<'a>, C<'a>), Inv<'a>>(); 674 | } 675 | 676 | #[test] 677 | fn validate_mixed<'a>() { 678 | #[allow(dead_code)] 679 | struct A<'a>(fn(&'a ())); 680 | unsafe impl<'a> Transient for A<'a> { 681 | type Static = A<'static>; 682 | type Transience = Co<'a>; 683 | } 684 | #[allow(dead_code)] 685 | struct B<'a>(fn(&'a ())); 686 | unsafe impl<'a> Transient for B<'a> { 687 | type Static = B<'static>; 688 | type Transience = Contra<'a>; 689 | } 690 | assert_can_transend_to::<(A<'a>, B<'a>), Inv<'a>>(); 691 | } 692 | } 693 | --------------------------------------------------------------------------------