cast_trait_object 0.1.2

Cast between trait objects using only safe Rust.
Documentation
[![docs.rs](https://docs.rs/cast_trait_object/badge.svg)](https://docs.rs/cast_trait_object)
[![crates.io](https://img.shields.io/crates/v/cast_trait_object.svg)](https://crates.io/crates/cast_trait_object)

# cast_trait_object

This crate offers functionality for casting between trait objects using only
safe Rust and no platform specific code. If you want to downcast to concrete
types instead of other trait objects then this crate can't help you, instead
use [`std::any`] or a crate like [`downcast-rs`].

This crate offers two things, a trait [`DynCast`] that abstracts over methods
used to cast between trait objects and some macros to minimize the boilerplate
needed to implement that trait.

<!-- Generate README.md using `cargo readme --no-license > README.md` -->
<!-- Generate documentation using `cargo +nightly doc --features docs` -->
<!-- Test all features using `cargo hack test --feature-powerset --skip full --skip default --skip docs --exclude-all-features` -->

## Usage

You should use the [`DynCast`] trait in trait bounds or as a supertrait and
then do casts using the methods provided by the [`DynCastExt`] trait. The
[`DynCast`] trait takes a type parameter that should be a "config" type
generated by the [`create_dyn_cast_config`] macro, this type defines from
which trait and to which trait a cast is made. Types that need to allow casting
to meet the [`DynCast`] trait bound can then implement it via the
[`impl_dyn_cast`] macro.

## Examples

```rust
use cast_trait_object::{create_dyn_cast_config, impl_dyn_cast, DynCast, DynCastExt};

create_dyn_cast_config!(SuperToSubCast = Super => Sub);
create_dyn_cast_config!(SuperUpcast = Super => Super);
trait Super: DynCast<SuperToSubCast> + DynCast<SuperUpcast> {}
trait Sub: Super {}

struct Foo;
impl Super for Foo {}
impl Sub for Foo {}
impl_dyn_cast!(Foo as Super => Sub, Super);

let foo: &dyn Super = &Foo;
// Casting to a sub trait is fallible (the error allows us to keep using the
// `dyn Super` trait object if we want which can be important if we are casting
// movable types like `Box<dyn Trait>`):
let foo: &dyn Sub = foo.dyn_cast().ok().unwrap();
// Upcasting to a supertrait is infallible:
let foo /*: &dyn Super*/ = foo.dyn_upcast::<dyn Super>();
```

When implementing the [`DynCast`] trait via the [`impl_dyn_cast`] macro you
can also list the created "config" types instead of the source and target
traits:

```rust
impl_dyn_cast!(Foo => SuperToSubCast, SuperUpcast);
```

If the `proc-macros` feature is enabled (which it is by default) we can also
use procedural attribute macros to write a little bit less boilerplate:

```rust
use cast_trait_object::{dyn_cast, dyn_upcast, DynCastExt};

#[dyn_cast(Sub)]
#[dyn_upcast]
trait Super {}
trait Sub: Super {}

struct Foo;
#[dyn_cast(Sub)]
#[dyn_upcast]
impl Super for Foo {}
impl Sub for Foo {}
```

Note that `#[dyn_upcast]` does the same as `#[dyn_cast(Super)]` but it is a bit
clearer about intentions:

```rust
use cast_trait_object::{dyn_cast, DynCastExt};

#[dyn_cast(Super, Sub)]
trait Super {}
trait Sub: Super {}

struct Foo;
#[dyn_cast(Super, Sub)]
impl Super for Foo {}
impl Sub for Foo {}

let foo: &dyn Sub = &Foo;
// Upcasting still works:
let foo /*: &dyn Super*/ = foo.dyn_upcast::<dyn Super>();
```

## Generics

Generics traits and types are supported and both the declarative macros
([`impl_dyn_cast`], [`create_dyn_cast_config`], [`impl_dyn_cast_config`])
and the procedural attribute macros ([`dyn_cast`] and [`dyn_upcast`]) can
be used with generics.

```rust
use cast_trait_object::{DynCastExt, dyn_cast, dyn_upcast};

// Define a source and target trait:
#[dyn_cast(Sub<T>)]
#[dyn_upcast]
trait Super<T> {}
trait Sub<T>: Super<T> {}

// Since `T` isn't used for `Color` it doesn't need to be `'static`:
struct Color(u8, u8, u8);
#[dyn_cast(Sub<T>)]
#[dyn_upcast]
impl<T> Super<T> for Color {}
impl<T> Sub<T> for Color {}

struct Example<T>(T);
#[dyn_cast(Sub<T>)]
#[dyn_upcast]
impl<T: 'static> Super<T> for Example<T> {}
impl<T: 'static> Sub<T> for Example<T> {}

let as_sub: &dyn Sub<bool> = &Example(false);
let upcasted: &dyn Super<bool> = as_sub.dyn_upcast();
let _downcasted /*: &dyn Sub<bool> */ = upcasted.dyn_cast::<dyn Sub<bool>>().ok().unwrap();
```

Note that one limitation of the current support for generic types is that if
the type that implements [`DynCast`] has any generic type parameters then
they might need to be constrained to be `'static`.

There is also another limitation with generic types and this one can be a bit
counter intuitive. The [`DynCast`] implementations that are generated by the
macros must always succeed or always fail. This means that if a target trait
is only implemented for a subset of the types that the [`DynCast`] trait is
implemented for then the cast will always fail.

```rust
use cast_trait_object::{create_dyn_cast_config, impl_dyn_cast, DynCast, DynCastExt};

// Define a source and target trait:
create_dyn_cast_config!(UpcastConfig = Super => Super);
create_dyn_cast_config!(SuperConfig = Super => Sub);
trait Super: DynCast<SuperConfig> + DynCast<UpcastConfig> {}
trait Sub: Super {}

/// Only implements `Sub` for types that implement `Display`.
struct OnlyDisplayGeneric<T>(T);
impl<T: 'static> Super for OnlyDisplayGeneric<T> {}
impl<T: core::fmt::Display + 'static> Sub for OnlyDisplayGeneric<T> {}
// The cast to `Sub` will always fail since this impl of DynCast includes
// some `T` that don't implement `Display`:
impl_dyn_cast!(for<T> OnlyDisplayGeneric<T> as Super where {T: 'static} => Sub);
impl_dyn_cast!(for<T> OnlyDisplayGeneric<T> as Super where {T: 'static} => Super);

// &str does implement Display:
let _is_display: &dyn core::fmt::Display = &"";

// But the cast will still fail:
let as_super: &dyn Super = &OnlyDisplayGeneric("");
assert!(as_super.dyn_cast::<dyn Sub>().is_err());

// `OnlyDisplayGeneric<&str>` does implement `Sub`:
let as_sub: &dyn Sub = &OnlyDisplayGeneric("");

// Note that this means that we can perform an upcast and then fail to downcast:
let upcasted: &dyn Super = as_sub.dyn_upcast();
assert!(upcasted.dyn_cast::<dyn Sub>().is_err());
```

The best way to avoid this problem is to have the same trait bounds on both
the source trait implementation and the target trait implementation.

## How it works

### How the conversion is preformed

Using the [`DynCast`] trait as a supertraits adds a couple of extra methods
to a trait object's vtable. These methods all essentially take a pointer to
the type and returns a new fat pointer which points to the wanted vtable.
There are a couple of methods since we need to generate one for each type of
trait object, so one for each of `&dyn Trait`, `&mut dyn Trait`,
`Box<dyn Trait>`, `Rc<dyn Trait>` and `Arc<dyn Trait>`. Note that these methods
are entirely safe Rust code, this crate doesn't use or generate any unsafe
code at all.

These methods work something like:

```rust
trait Super {}
trait Sub {
    fn upcast(self: Box<Self>) -> Box<dyn Super>;
}

impl Super for () {}
impl Sub for () {
    fn upcast(self: Box<Self>) -> Box<dyn Super> { self }
}

let a: Box<dyn Sub> = Box::new(());
let a: Box<dyn Super> = a.upcast();
```

The [`DynCastExt`] trait then abstracts over the different types of trait
objects so that when a call is made using the [dyn_cast](DynCastExt::dyn_cast)
method the compiler can inline that static method call to the correct method
on the trait object.

### Why "config" types are needed

We have to generate "config" types since we need to uniquely identify each
[`DynCast`] supertrait based on which trait it is casting from and into.
Originally this was just done using two type parameters on the trait, something
like `DynCast<dyn Super, dyn Sub>`, but that caused compile errors when they were
used as a supertrait of one of the mentioned traits. So now the traits are
"hidden" as associated types on a generated "config" type. To make this "config"
type more ergonomic we also implement a [`GetDynCastConfig`] trait to easily
go from the source trait and target trait to a "config" type via something
like `<dyn Source as GetDynCastConfig<dyn Target>>::Config`. This allows
the macros ([`impl_dyn_cast`], [`dyn_cast`] and [`dyn_upcast`]) to take traits
as arguments instead of "config" types, it also makes type inference work for
the [`DynCastExt`] trait.

### How the macros know if a type implements a "target" trait or not

When a type implementing [`DynCast`] for a specific config and therefore
source to target trait cast the generated code must choose if the cast is
going to succeed or not. We want to return `Ok(value as &dyn Target)` if
the type implements the `Target` trait and `Err(value as &dyn Source)` if
it doesn't.

We can use a clever hack to only preform the coercion if a type actually
implements the target trait. See dtolnay's [Autoref-based stable specialization]
(https://github.com/dtolnay/case-studies/tree/master/autoref-specialization)
case study for more information about how this hack works. In short the hack
allows us to call one method if a trait bound is met and another method if it
isn't. In this way we can call a helper method that performs the coercion to
the target trait only if the type actually implements that trait.

So we could generate something like:

```rust
trait Source {}
trait Target {}

struct Foo;
impl Source for Foo {}

struct Fallback;
impl Fallback {
    fn cast<'a, T: Source>(&self, value: &'a T) -> &'a dyn Source { value }
}

struct HasTrait<T>(core::marker::PhantomData<T>);
impl<T> HasTrait<T> {
    fn new() -> Self {
         Self(core::marker::PhantomData)
    }
}
impl<T: Target> HasTrait<T> {
    fn cast<'a>(&self, value: &'a T) -> &'a dyn Target { value }
}
impl<T> core::ops::Deref for HasTrait<T> {
    type Target = Fallback;
    fn deref(&self) -> &Self::Target {
        static FALLBACK: Fallback = Fallback;
        &FALLBACK
    }
}

let used_fallback: &dyn Source = HasTrait::<Foo>::new().cast(&Foo);
```

So the [`impl_dyn_cast`] macro works by generating a struct that implements
[`core::ops::Deref`] into another type. Both types have a `cast` method but
they do different things. The first struct's `cast` method has a trait bound
so that it is only implemented if the cast can succeed. If the first method
can't be used the compiler will insert a deref operation (`&*foo`) and see
if there is a method that can apply after that. In this case that means that
the `Fallback` structs method is called. This way the generated code doesn't
call the method that preform the coercion to the `Target` trait unless the
type actually implements it.

## Alternatives

The [`intertrait`] crate offers similar functionality to this crate but has
a totally different implementation, at least as of [`intertrait`] version
`0.2.0`. It uses the [`linkme`] crate to create a registry of [`std::any::Any`]
type ids for types that can be cast into a certain trait object. This means
it probably has some runtime overhead when it looks up a cast function in
the global registry using a [`TypeId`]. It also means that it can't work on
all platforms since the [`linkme`] crate needs to offer support for them. This
is a limitation that this crate doesn't have.

The [`traitcast`] crate works similar to [`intertrait`] in that it has a
global registry that is keyed with [`TypeId`]s. But it differs in that it
uses the [`inventory`] crate to build the registry instead of the [`linkme`]
crate. The [`inventory`] crate uses the [`ctor`] crate to run some code before
`main`, something that is generally discouraged and this is something that
[`intertrait`] actually mentions as an advantage to its approach.

The [`traitcast_core`] library allow for a more low level API that doesn't
depend on a global registry and therefore also doesn't depend on a crate like
[`linkme`] or [`inventory`] that needs platform specific support. Instead it
requires that you explicitly create a registry and register all your types
and their casts with it.

The [`downcast-rs`] crate offers downcasting to concrete types but not
directly casting from one trait object to another trait object. So it has a
different use case and both it and this crate could be useful in the same
project.

You could just define methods on your traits similar to the ones provided by
this crate's [`DynCast`] trait. Doing this yourself can be more flexible and
you could for example minimize bloat by only implementing methods for casts
that you actually require. The disadvantage is that it would be much less
ergonomic than what this crate offers.

## References

The following GutHub issue [Clean up pseudo-downcasting from VpnProvider supertrait to subtraits with better solution · Issue #21 · jamesmcm/vopono](https://github.com/jamesmcm/vopono/issues/21)
inspired this library.

This library was mentioned in the following blog post in the "Upcasting"
section:
[So you want to write object oriented Rust :: Darrien's Blog — Dev work and musings](https://blog.darrien.dev/posts/so-you-want-to-object/#upcasting)

## License

This project is released under either:

- [MIT License]https://github.com/Lej77/cast_trait_object/blob/master/LICENSE-MIT
- [Apache License (Version 2.0)]https://github.com/Lej77/cast_trait_object/blob/master/LICENSE-APACHE

at your choosing.

[`std::any`]: https://doc.rust-lang.org/std/any
[`std::any::Any`]: https://doc.rust-lang.org/std/any/trait.Any.html
[`TypeId`]: https://doc.rust-lang.org/std/any/struct.TypeId.html
[`downcast-rs`]: https://crates.io/crates/downcast-rs
[`intertrait`]: https://crates.io/crates/intertrait
[`traitcast`]: https://crates.io/crates/traitcast
[`traitcast_core`]: https://crates.io/crates/traitcast_core
[`linkme`]: https://crates.io/crates/linkme
[`inventory`]: https://crates.io/crates/inventory
[`ctor`]: https://crates.io/crates/ctor

### Contribution

Unless you explicitly state otherwise, any contribution intentionally
submitted for inclusion in the work by you, as defined in the Apache-2.0
license, shall be dual licensed as above, without any additional terms or
conditions.