# downcast-rs [![Build status](https://img.shields.io/github/workflow/status/marcianx/downcast-rs/CI/master)](https://github.com/marcianx/downcast-rs/actions) [![Latest version](https://img.shields.io/crates/v/downcast-rs.svg)](https://crates.io/crates/downcast-rs) [![Documentation](https://docs.rs/downcast-rs/badge.svg)](https://docs.rs/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**. ## 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 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 = Box::new(Foo(42)); // Try sequential downcasts. if let Some(foo) = base.downcast_ref::() { assert_eq!(foo.0, 42); } else if let Some(bar) = base.downcast_ref::() { assert_eq!(bar.0, 42.0); } assert!(base.is::()); // Fail to convert `Box` into `Box`. let res = base.downcast::(); assert!(res.is_err()); let base = res.unwrap_err(); // Convert `Box` into `Box`. assert_eq!(42, base.downcast::().map_err(|_| "Shouldn't happen.").unwrap().0); // Also works with `Rc`. let mut rc: Rc = Rc::new(Foo(42)); assert_eq!(42, rc.downcast_rc::().map_err(|_| "Shouldn't happen.").unwrap().0); // Since this trait is `Sync`, it also supports `Arc` downcasts. let mut arc: Arc = Arc::new(Foo(42)); assert_eq!(42, arc.downcast_arc::().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 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: Downcast { type H: Copy; } downcast_rs::impl_downcast!(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::() { assert_eq!(foo.0, 42); } else if let Some(bar) = base.downcast_ref::() { assert_eq!(bar.0, 42.0); } assert!(base.is::()); } ``` ## License Copyright 2020, Ashish Myles (maintainer) and contributors. This software is dual-licensed under the [MIT](LICENSE-MIT) and [Apache 2.0](LICENSE-APACHE) 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.