MCPcopy Index your code
hub / github.com/JRRudy1/transient

github.com/JRRudy1/transient @v0.4.1

Chat with this repo
repository ↗ · DeepWiki ↗ · release v0.4.1 ↗ · + Follow
175 symbols 260 edges 39 files 22 documented · 13% updated 18mo agov0.4.1 · 2024-09-08★ 435 open issues
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

transient

This crate provides a reimplementation of the std::any::Any trait supporting types with non-'static lifetimes.

Crates.io

Documentation

Module documentation with examples

Usage

To bring this crate into your repository, either add transient to your Cargo.toml, or run cargo add transient.

Using this crate starts by implementing the provided Transient trait for a type, which can be derived using the included derive macro or implemented by hand by simply defining two associated types. Implementing this trait manually is unsafe but straightforward and extensively documented, and once implemented it enables this crate's functionality to be used safely.

The following example demonstrates the trivial case of deriving the Transient trait for a 'static type, and then casting it to a dyn Any trait object to emulate dynamic typing just as you would using the stdlib's implementation:

use transient::{Transient, Any, Downcast};

#[derive(Transient, Debug, PartialEq)]
struct MyUsize(usize);

fn main() {
    let orig = MyUsize(5);
    // we can erase the 'static type...
    let erased: &dyn Any = &orig;
    assert!(erased.is::<MyUsize>());
    // and restore it...
    let restored: &MyUsize = erased.downcast_ref().unwrap();
    assert_eq!(restored, &orig);
    // and use it in dynamically-typed shenanigans...
    let bananas = "bananas".to_string();
    let stuff = vec![erased, &orig.0, restored, &bananas];
    assert_eq!(stuff[0].downcast_ref::<MyUsize>().unwrap(), &orig);
}

Where it get's interesting is when you have a non-'static type containing borrowed data, which would be ineligable for use with the std::any::Any implementation due to its 'static bound. The following example will demonstrate using the transient crate's implementation to utilize the same functionality as for 'static types by simply parameterizing the Any trait by Inv, which is a Transience implementation that binds the lifetime and variance information that the stdlib would not be able to handle:

use transient::{Transient, Any, Inv, Downcast};

#[derive(Transient, Debug, PartialEq)]
struct MyUsizeRef<'a>(&'a usize);

fn main() {
    let five = 5;
    let orig = MyUsizeRef(&five);
    // we can erase the non-'static type...
    let erased: &dyn Any<Inv> = &orig;
    assert!(erased.is::<MyUsizeRef>());
    // and restore it...
    let restored: &MyUsizeRef = erased.downcast_ref().unwrap();
    assert_eq!(restored, &orig);
    // and use it in dynamically-typed shenanigans...
    let stuff = vec![erased, &five, restored, &"bananas"];
    assert_eq!(stuff[0].downcast_ref::<MyUsizeRef>().unwrap(), &orig);
}

The Inv type used above stands for "invariant", which is the most conservative form of a property known as variance that describes the behavior of a type with respect to a lifetime parameter. An understanding of variance will let you utilize the advanced features of this crate, but is not necessary for most purposes since the Inv type shown above be safely used for any type with a single lifetime parameter.

In the first example where we cast our type to a naked dyn Any without specifying a Transience type, the Any trait's default type parameter () was chosen implicitly which causes it to behave like the stdlib's implementation by only accepting 'static types; trying this with MyUsizeRef defined in the second example would have been rejected by the compiler. This hints at the underlying mechanism used to implement this crate, wherein types declare their temporal relationships (i.e. Transience) when implementing the Transient trait, which then bounds the range of dyn Any<_> flavors they are allowed to utilize. Non-'static types with a single lifetime 'a that implement Transient using the derive macro are (by default) assigned a Transience of Inv<'a>, which limits them to being erased to (and restored from) the dyn Any<Inv<'a>> trait object. By contrast, 'static types implement the most flexible Transience of () which allows them to be be cast to any dyn Any<_> they want, up to and including the default dyn Any().

There is a large amount of middle-ground between these two extremes which is discussed in-depth throughout the documentation (hint - there are Co and Contra types as well), but the key takeaway is that types make a single unsafe but straight-forward decision about their temporal behavior when implementing the Transient trait, and then everything else is kept safe using the type system and trait bounds.

Usage: multiple life parameters

The mechanism demonstrated above extends naturally to types with more than one lifetime parameter by instead parameterizing the dyn Any<_> with a tuple as shown in the following example; however, the included derive macro currently only supports types with zero or one lifetime parameters, so we will implement the Transient trait ourselves this time:

use transient::{Transient, Any, Inv, Downcast};

#[derive(Debug, PartialEq)]
struct TwoRefs<'a, 'b>(&'a i32, &'b i32);

unsafe impl<'a, 'b> Transient for TwoRefs<'a, 'b> {
    // the `Static` type is simply the same as the `Self` type with its 
    // lifetimes replaced by `'static`
    type Static = TwoRefs<'static, 'static>;
    // we use a tuple for the `Transience` that covers both lifetimes, using 
    // `Inv` for each element since this is always safe
    type Transience = (Inv<'a>, Inv<'b>);
}

fn main() {
    let (five, seven) = (5, 7);
    let orig = TwoRefs(&five, &seven);
    let erased: &dyn Any<Inv> = &orig;
    assert!(erased.is::<TwoRefs>());
    let restored: &TwoRefs = erased.downcast_ref().unwrap();
    assert_eq!(restored, &orig);
    let stuff = vec![erased, &five, restored, &"bananas"];
    assert_eq!(stuff[0].downcast_ref::<TwoRefs>().unwrap(), &orig);
}

License

This project is licensed under either of

  • Apache License, Version 2.0, (LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0)
  • MIT license (LICENSE-MIT or https://opensource.org/licenses/MIT)

at your option.

Extension points exported contracts — how you extend this code

Transient (Interface)
Unsafe trait defining the lifetime-relationships of a potentially non-`'static` type so that it can be safely erased to [56 …
src/transient.rs
CanTranscendTo (Interface)
Unsafe marker trait indicating that the implementing [`Transience`] can safely upcast to the parameterizing `Transience` [6 …
src/transience.rs
Any (Interface)
A trait to emulate dynamic typing, modeled after the [`std::any::Any`] trait with added support for non-`'static` types. [1 …
src/any.rs
Static (Interface)
Safe trait that `'static` types can implement to get a free blanket impl of the [`Transient`] trait. Implementing this [15 …
src/transient.rs
CanRecoverFrom (Interface)
Unsafe marker trait indicating that the implementing [`Transience`] can be safely recovered from the parameterizing `Tra [8 …
src/transience.rs
Downcast (Interface)
Extension trait defining methods for downcasting the [`dyn Any<_>`][Any] trait object back into a concrete type. This t
src/any.rs
Transience (Interface)
Marker trait for types used to establish the [variance] of a type with respect to each of its lifetime parameters, inclu [4 …
src/transience.rs

Core symbols most depended-on inside this repo

span
called by 5
derive/src/variance.rs
erase
called by 4
src/transient.rs
erase_ref
called by 4
src/transient.rs
erase_mut
called by 4
src/transient.rs
push
called by 4
derive/src/variance.rs
span
called by 3
derive/src/lib.rs
contains_static_bound
called by 2
derive/src/utils.rs
ensure_empty
called by 2
derive/src/variance.rs

Shape

Class 77
Function 50
Method 37
Interface 7
Enum 4

Languages

Rust100%

Modules by API surface

derive/src/lib.rs15 symbols
src/any.rs14 symbols
derive/src/variance.rs13 symbols
derive/tests/expand/02-covariance.expanded.rs8 symbols
derive/src/utils.rs8 symbols
src/transient.rs7 symbols
src/tests.rs7 symbols
src/transience.rs6 symbols
derive/tests/expand/04-mixed-variance.expanded.rs6 symbols
derive/tests/expand/03-contravariance.expanded.rs6 symbols
derive/tests/expand/02-covariance.rs6 symbols
derive/src/options.rs6 symbols

For agents

$ claude mcp add transient \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact