#downcast #any #trait #associated #constraint

downcast-rs

Trait object downcasting support using only safe Rust. It supports type parameters, associated types, and type constraints.

10 releases (stable)

1.1.1 Oct 28, 2019
1.0.4 Apr 9, 2019
1.0.3 May 22, 2018
1.0.1 Mar 9, 2018
0.1.0 Dec 16, 2015

#10 in Rust patterns

Download history 5350/week @ 2019-08-15 5218/week @ 2019-08-22 5703/week @ 2019-08-29 6624/week @ 2019-09-05 5988/week @ 2019-09-12 6331/week @ 2019-09-19 7397/week @ 2019-09-26 9061/week @ 2019-10-03 8647/week @ 2019-10-10 8195/week @ 2019-10-17 12762/week @ 2019-10-24 10580/week @ 2019-10-31 11342/week @ 2019-11-07 12403/week @ 2019-11-14 11997/week @ 2019-11-21

28,812 downloads per month
Used in 295 crates (26 directly)

MIT/Apache

31KB
433 lines

downcast-rs

Rust enums are great for types where all variations are known beforehand. But a container of user-defined types requires an open-ended type like a trait object. Some applications may want to cast these trait objects back to the original concrete types to access additional functionality and performant inlined implementations.

downcast-rs adds this downcasting support to trait objects using only safe Rust. It supports type parameters, associated types, and constraints.

To make a trait downcastable, make it extend either downcast::Downcast or downcast::DowncastSync and invoke impl_downcast! on it as in the examples below.

Since 1.1.0, the minimum supported Rust version is 1.33 to support Rc and Arc in the receiver position.

trait Trait: Downcast {}
impl_downcast!(Trait);

// Also supports downcasting `Arc`-ed trait objects by extending `DowncastSync`
// and starting `impl_downcast!` with `sync`.
trait TraitSync: DowncastSync {}
impl_downcast!(sync TraitSync);

// With type parameters.
trait TraitGeneric1<T>: Downcast {}
impl_downcast!(TraitGeneric1<T>);

// With associated types.
trait TraitGeneric2: Downcast { type G; type H; }
impl_downcast!(TraitGeneric2 assoc G, H);

// With constraints on types.
trait TraitGeneric3<T: Copy>: Downcast {
    type H: Clone;
}
impl_downcast!(TraitGeneric3<T> assoc H where T: Copy, H: Clone);

// With concrete types.
trait TraitConcrete1<T: Copy>: Downcast {}
impl_downcast!(concrete TraitConcrete1<u32>);

trait TraitConcrete2<T: Copy>: Downcast { type H; }
impl_downcast!(concrete TraitConcrete2<u32> assoc H=f64);

Example without generics

// Import macro via `macro_use` pre-1.30.
#[macro_use]
extern crate downcast_rs;
use downcast_rs::DowncastSync;

// To create a trait with downcasting methods, extend `Downcast` or `DowncastSync`
// and run `impl_downcast!()` on the trait.
trait Base: DowncastSync {}
impl_downcast!(sync Base);  // `sync` => also produce `Arc` downcasts.

// Concrete types implementing Base.
#[derive(Debug)]
struct Foo(u32);
impl Base for Foo {}
#[derive(Debug)]
struct Bar(f64);
impl Base for Bar {}

fn main() {
    // Create a trait object.
    let mut base: Box<Base> = Box::new(Foo(42));

    // Try sequential downcasts.
    if let Some(foo) = base.downcast_ref::<Foo>() {
        assert_eq!(foo.0, 42);
    } else if let Some(bar) = base.downcast_ref::<Bar>() {
        assert_eq!(bar.0, 42.0);
    }

    assert!(base.is::<Foo>());

    // Fail to convert `Box<Base>` into `Box<Bar>`.
    let res = base.downcast::<Bar>();
    assert!(res.is_err());
    let base = res.unwrap_err();
    // Convert `Box<Base>` into `Box<Foo>`.
    assert_eq!(42, base.downcast::<Foo>().map_err(|_| "Shouldn't happen.").unwrap().0);

    // Also works with `Rc`.
    let mut rc: Rc<Base> = Rc::new(Foo(42));
    assert_eq!(42, rc.downcast_rc::<Foo>().map_err(|_| "Shouldn't happen.").unwrap().0);

    // Since this trait is `Sync`, it also supports `Arc` downcasts.
    let mut arc: Arc<Base> = Arc::new(Foo(42));
    assert_eq!(42, arc.downcast_arc::<Foo>().map_err(|_| "Shouldn't happen.").unwrap().0);
}

Example with a generic trait with associated types and constraints

// Can call macro via namespace since rust 1.30.
extern crate downcast_rs;
use downcast_rs::Downcast;

// To create a trait with downcasting methods, extend `Downcast` or `DowncastSync`
// and run `impl_downcast!()` on the trait.
trait Base<T: Clone>: Downcast { type H: Copy; }
downcast_rs::impl_downcast!(Base<T> assoc H where T: Clone, H: Copy);
// or: impl_downcast!(concrete Base<u32> assoc H=f32)

// Concrete types implementing Base.
struct Foo(u32);
impl Base<u32> for Foo { type H = f32; }
struct Bar(f64);
impl Base<u32> for Bar { type H = f32; }

fn main() {
    // Create a trait object.
    let mut base: Box<Base<u32, H=f32>> = Box::new(Bar(42.0));

    // Try sequential downcasts.
    if let Some(foo) = base.downcast_ref::<Foo>() {
        assert_eq!(foo.0, 42);
    } else if let Some(bar) = base.downcast_ref::<Bar>() {
        assert_eq!(bar.0, 42.0);
    }

    assert!(base.is::<Bar>());
}

License

Copyright 2015, Ashish Myles. This software is dual-licensed under the MIT and Apache 2.0 licenses.

No runtime deps