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.
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
// With associated types. trait TraitGeneric2: Downcast { type G; type H; } impl_downcast!(TraitGeneric2 assoc G, H);
// With constraints on types.
trait TraitGeneric3
// With concrete types.
trait TraitConcrete1
trait TraitConcrete2
``rust
// Import macro via
macro_use` pre-1.30.
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.
struct Foo(u32); impl Base for Foo {}
struct Bar(f64); impl Base for Bar {}
fn main() {
// Create a trait object.
let mut base: Box
// 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);
} ```
```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
// Concrete types implementing Base.
struct Foo(u32);
impl Base
fn main() {
// Create a trait object.
let mut base: Box
// 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>());
} ```
Copyright 2020, Ashish Myles (maintainer) and contributors. This software is dual-licensed under the MIT and Apache 2.0 licenses.
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.