README.md
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
README.tpl
1{{readme}}
2
3## Why no changes in a while?
4
5This library is a thoroughly-tested boilerplate generator, is code complete, has
6no unsafe, and is vanishingly unlikely to have any security issues to patch.
7
8## License
9
10Copyright 2020, Ashish Myles (maintainer) and contributors.
11This software is dual-licensed under the [MIT](LICENSE-MIT) and
12[Apache 2.0](LICENSE-APACHE) licenses.
13
14### Contribution
15
16Unless you explicitly state otherwise, any contribution intentionally submitted
17for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
18dual licensed as above, without any additional terms or conditions.
19