downcast-rs

Build status Latest version Documentation

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.

Usage

Add the following to your Cargo.toml:

toml [dependencies] downcast-rs = "1.2.0"

This crate is no_std compatible. To use it without std:

toml [dependencies] downcast-rs = { version = "1.2.0", default-features = false }

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.

```rust 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: Downcast {} impl_downcast!(TraitGeneric1);

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

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

// With concrete types. trait TraitConcrete1: Downcast {} impl_downcast!(concrete TraitConcrete1);

trait TraitConcrete2: Downcast { type H; } impl_downcast!(concrete TraitConcrete2 assoc H=f64); ```

Example without generics

``rust // Import macro viamacro_use` pre-1.30.

[macro_use]

extern crate downcastrs; use downcastrs::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 = 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

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

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

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

fn main() { // Create a trait object. let mut base: Box> = 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 2020, Ashish Myles (maintainer) and contributors. This software is dual-licensed under the MIT and Apache 2.0 licenses.

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.