1# downcast-rs
2
3[![Build status](https://img.shields.io/github/actions/workflow/status/marcianx/downcast-rs/main.yml?branch=master)](https://github.com/marcianx/downcast-rs/actions)
4[![Latest version](https://img.shields.io/crates/v/downcast-rs.svg)](https://crates.io/crates/downcast-rs)
5[![Documentation](https://docs.rs/downcast-rs/badge.svg)](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 = "1.2.1"
23```
24
25This crate is `no_std` compatible. To use it without `std`:
26
27```toml
28[dependencies]
29downcast-rs = { version = "1.2.0", 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 1.2.0, the minimum supported Rust version is 1.36 due to needing stable access to alloc.
37
38```rust
39trait Trait: Downcast {}
40impl_downcast!(Trait);
41
42// Also supports downcasting `Arc`-ed trait objects by extending `DowncastSync`
43// and starting `impl_downcast!` with `sync`.
44trait TraitSync: DowncastSync {}
45impl_downcast!(sync TraitSync);
46
47// With type parameters.
48trait TraitGeneric1<T>: Downcast {}
49impl_downcast!(TraitGeneric1<T>);
50
51// With associated types.
52trait TraitGeneric2: Downcast { type G; type H; }
53impl_downcast!(TraitGeneric2 assoc G, H);
54
55// With constraints on types.
56trait TraitGeneric3<T: Copy>: Downcast {
57    type H: Clone;
58}
59impl_downcast!(TraitGeneric3<T> assoc H where T: Copy, H: Clone);
60
61// With concrete types.
62trait TraitConcrete1<T: Copy>: Downcast {}
63impl_downcast!(concrete TraitConcrete1<u32>);
64
65trait TraitConcrete2<T: Copy>: Downcast { type H; }
66impl_downcast!(concrete TraitConcrete2<u32> assoc H=f64);
67```
68
69## Example without generics
70
71```rust
72// Import macro via `macro_use` pre-1.30.
73#[macro_use]
74extern crate downcast_rs;
75use downcast_rs::DowncastSync;
76
77// To create a trait with downcasting methods, extend `Downcast` or `DowncastSync`
78// and run `impl_downcast!()` on the trait.
79trait Base: DowncastSync {}
80impl_downcast!(sync Base);  // `sync` => also produce `Arc` downcasts.
81
82// Concrete types implementing Base.
83#[derive(Debug)]
84struct Foo(u32);
85impl Base for Foo {}
86#[derive(Debug)]
87struct Bar(f64);
88impl Base for Bar {}
89
90fn main() {
91    // Create a trait object.
92    let mut base: Box<Base> = Box::new(Foo(42));
93
94    // Try sequential downcasts.
95    if let Some(foo) = base.downcast_ref::<Foo>() {
96        assert_eq!(foo.0, 42);
97    } else if let Some(bar) = base.downcast_ref::<Bar>() {
98        assert_eq!(bar.0, 42.0);
99    }
100
101    assert!(base.is::<Foo>());
102
103    // Fail to convert `Box<Base>` into `Box<Bar>`.
104    let res = base.downcast::<Bar>();
105    assert!(res.is_err());
106    let base = res.unwrap_err();
107    // Convert `Box<Base>` into `Box<Foo>`.
108    assert_eq!(42, base.downcast::<Foo>().map_err(|_| "Shouldn't happen.").unwrap().0);
109
110    // Also works with `Rc`.
111    let mut rc: Rc<Base> = Rc::new(Foo(42));
112    assert_eq!(42, rc.downcast_rc::<Foo>().map_err(|_| "Shouldn't happen.").unwrap().0);
113
114    // Since this trait is `Sync`, it also supports `Arc` downcasts.
115    let mut arc: Arc<Base> = Arc::new(Foo(42));
116    assert_eq!(42, arc.downcast_arc::<Foo>().map_err(|_| "Shouldn't happen.").unwrap().0);
117}
118```
119
120## Example with a generic trait with associated types and constraints
121
122```rust
123// Can call macro via namespace since rust 1.30.
124extern crate downcast_rs;
125use downcast_rs::Downcast;
126
127// To create a trait with downcasting methods, extend `Downcast` or `DowncastSync`
128// and run `impl_downcast!()` on the trait.
129trait Base<T: Clone>: Downcast { type H: Copy; }
130downcast_rs::impl_downcast!(Base<T> assoc H where T: Clone, H: Copy);
131// or: impl_downcast!(concrete Base<u32> assoc H=f32)
132
133// Concrete types implementing Base.
134struct Foo(u32);
135impl Base<u32> for Foo { type H = f32; }
136struct Bar(f64);
137impl Base<u32> for Bar { type H = f32; }
138
139fn main() {
140    // Create a trait object.
141    let mut base: Box<Base<u32, H=f32>> = Box::new(Bar(42.0));
142
143    // Try sequential downcasts.
144    if let Some(foo) = base.downcast_ref::<Foo>() {
145        assert_eq!(foo.0, 42);
146    } else if let Some(bar) = base.downcast_ref::<Bar>() {
147        assert_eq!(bar.0, 42.0);
148    }
149
150    assert!(base.is::<Bar>());
151}
152```
153
154## Why no changes in a while?
155
156This library is a thoroughly-tested boilerplate generator, is code complete, has
157no unsafe, and is vanishingly unlikely to have any security issues to patch.
158
159## License
160
161Copyright 2020, Ashish Myles (maintainer) and contributors.
162This software is dual-licensed under the [MIT](LICENSE-MIT) and
163[Apache 2.0](LICENSE-APACHE) licenses.
164
165### Contribution
166
167Unless you explicitly state otherwise, any contribution intentionally submitted
168for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
169dual licensed as above, without any additional terms or conditions.
170