1# downcast-rs 2 3[](https://github.com/marcianx/downcast-rs/actions) 4[](https://crates.io/crates/downcast-rs) 5[](https://docs.rs/downcast-rs) 6 7Rust enums are great for types where all variations are known beforehand. But a 8container of user-defined types requires an open-ended type like a **trait 9object**. Some applications may want to cast these trait objects back to the 10original concrete types to access additional functionality and performant 11inlined implementations. 12 13`downcast-rs` adds this downcasting support to trait objects using only safe 14Rust. It supports **type parameters**, **associated types**, and **constraints**. 15 16## Usage 17 18Add the following to your `Cargo.toml`: 19 20```toml 21[dependencies] 22downcast-rs = "2.0.1" 23``` 24 25This crate is `no_std` compatible. To use it without `std`: 26 27```toml 28[dependencies] 29downcast-rs = { version = "2.0.1", default-features = false } 30``` 31 32To make a trait downcastable, make it extend either `downcast::Downcast` or 33`downcast::DowncastSync` and invoke `impl_downcast!` on it as in the examples 34below. 35 36Since 2.0.0, the minimum supported Rust version is 1.56. 37 38```rust 39# use downcast_rs::{Downcast, impl_downcast}; 40# #[cfg(feature = "sync")] 41# use downcast_rs::DowncastSync; 42trait Trait: Downcast {} 43impl_downcast!(Trait); 44 45// Also supports downcasting `Arc`-ed trait objects by extending `DowncastSync` 46// and starting `impl_downcast!` with `sync`. 47# #[cfg(feature = "sync")] 48trait TraitSync: DowncastSync {} 49# #[cfg(feature = "sync")] 50impl_downcast!(sync TraitSync); 51 52// With type parameters. 53trait TraitGeneric1<T>: Downcast {} 54impl_downcast!(TraitGeneric1<T>); 55 56// With associated types. 57trait TraitGeneric2: Downcast { type G; type H; } 58impl_downcast!(TraitGeneric2 assoc G, H); 59 60// With constraints on types. 61trait TraitGeneric3<T: Copy>: Downcast { 62 type H: Clone; 63} 64impl_downcast!(TraitGeneric3<T> assoc H where T: Copy, H: Clone); 65 66// With concrete types. 67trait TraitConcrete1<T: Copy>: Downcast {} 68impl_downcast!(concrete TraitConcrete1<u32>); 69 70trait TraitConcrete2<T: Copy>: Downcast { type H; } 71impl_downcast!(concrete TraitConcrete2<u32> assoc H=f64); 72# fn main() {} 73``` 74 75## Example without generics 76 77```rust 78# use std::rc::Rc; 79# #[cfg(feature = "sync")] 80# use std::sync::Arc; 81# use downcast_rs::impl_downcast; 82# #[cfg(not(feature = "sync"))] 83# use downcast_rs::Downcast; 84# #[cfg(feature = "sync")] 85use downcast_rs::DowncastSync; 86 87// To create a trait with downcasting methods, extend `Downcast` or `DowncastSync` 88// and run `impl_downcast!()` on the trait. 89# #[cfg(not(feature = "sync"))] 90# trait Base: Downcast {} 91# #[cfg(not(feature = "sync"))] 92# impl_downcast!(Base); 93# #[cfg(feature = "sync")] 94trait Base: DowncastSync {} 95# #[cfg(feature = "sync")] 96impl_downcast!(sync Base); // `sync` => also produce `Arc` downcasts. 97 98// Concrete types implementing Base. 99#[derive(Debug)] 100struct Foo(u32); 101impl Base for Foo {} 102#[derive(Debug)] 103struct Bar(f64); 104impl Base for Bar {} 105 106fn main() { 107 // Create a trait object. 108 let mut base: Box<dyn Base> = Box::new(Foo(42)); 109 110 // Try sequential downcasts. 111 if let Some(foo) = base.downcast_ref::<Foo>() { 112 assert_eq!(foo.0, 42); 113 } else if let Some(bar) = base.downcast_ref::<Bar>() { 114 assert_eq!(bar.0, 42.0); 115 } 116 117 assert!(base.is::<Foo>()); 118 119 // Fail to convert `Box<dyn Base>` into `Box<Bar>`. 120 let res = base.downcast::<Bar>(); 121 assert!(res.is_err()); 122 let base = res.unwrap_err(); 123 // Convert `Box<dyn Base>` into `Box<Foo>`. 124 assert_eq!(42, base.downcast::<Foo>().map_err(|_| "Shouldn't happen.").unwrap().0); 125 126 // Also works with `Rc`. 127 let mut rc: Rc<dyn Base> = Rc::new(Foo(42)); 128 assert_eq!(42, rc.downcast_rc::<Foo>().map_err(|_| "Shouldn't happen.").unwrap().0); 129 130 // Since this trait is `Sync`, it also supports `Arc` downcasts. 131 # #[cfg(feature = "sync")] 132 let mut arc: Arc<dyn Base> = Arc::new(Foo(42)); 133 # #[cfg(feature = "sync")] 134 assert_eq!(42, arc.downcast_arc::<Foo>().map_err(|_| "Shouldn't happen.").unwrap().0); 135} 136``` 137 138## Example with a generic trait with associated types and constraints 139 140```rust 141use downcast_rs::{Downcast, impl_downcast}; 142 143// To create a trait with downcasting methods, extend `Downcast` or `DowncastSync` 144// and run `impl_downcast!()` on the trait. 145trait Base<T: Clone>: Downcast { type H: Copy; } 146impl_downcast!(Base<T> assoc H where T: Clone, H: Copy); 147// or: impl_downcast!(concrete Base<u32> assoc H=f32) 148 149// Concrete types implementing Base. 150struct Foo(u32); 151impl Base<u32> for Foo { type H = f32; } 152struct Bar(f64); 153impl Base<u32> for Bar { type H = f32; } 154 155fn main() { 156 // Create a trait object. 157 let mut base: Box<dyn Base<u32, H=f32>> = Box::new(Bar(42.0)); 158 159 // Try sequential downcasts. 160 if let Some(foo) = base.downcast_ref::<Foo>() { 161 assert_eq!(foo.0, 42); 162 } else if let Some(bar) = base.downcast_ref::<Bar>() { 163 assert_eq!(bar.0, 42.0); 164 } 165 166 assert!(base.is::<Bar>()); 167} 168``` 169 170## Why no changes in a while? 171 172This library is a thoroughly-tested boilerplate generator, is code complete, has 173no unsafe, and is vanishingly unlikely to have any security issues to patch. 174 175## License 176 177Copyright 2020, Ashish Myles (maintainer) and contributors. 178This software is dual-licensed under the [MIT](LICENSE-MIT) and 179[Apache 2.0](LICENSE-APACHE) licenses. 180 181### Contribution 182 183Unless you explicitly state otherwise, any contribution intentionally submitted 184for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 185dual licensed as above, without any additional terms or conditions. 186