aboutsummaryrefslogtreecommitdiff
path: root/README.md
blob: 2c462f7f3b876c57be2886b16aebea97f5ef82a9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# 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<T>: Downcast {}
impl_downcast!(TraitGeneric1<T>);

// With associated types.
trait TraitGeneric2: Downcast { type G; type H; }
impl_downcast!(TraitGeneric2 assoc G, H);

// With constraints on types.
trait TraitGeneric3<T: Copy>: Downcast {
    type H: Clone;
}
impl_downcast!(TraitGeneric3<T> assoc H where T: Copy, H: Clone);

// With concrete types.
trait TraitConcrete1<T: Copy>: Downcast {}
impl_downcast!(concrete TraitConcrete1<u32>);

trait TraitConcrete2<T: Copy>: Downcast { type H; }
impl_downcast!(concrete TraitConcrete2<u32> 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<Base> = Box::new(Foo(42));

    // 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);
}
```

## 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<T: Clone>: Downcast { type H: Copy; }
downcast_rs::impl_downcast!(Base<T> assoc H where T: Clone, H: Copy);
// or: impl_downcast!(concrete Base<u32> assoc H=f32)

// Concrete types implementing Base.
struct Foo(u32);
impl Base<u32> for Foo { type H = f32; }
struct Bar(f64);
impl Base<u32> for Bar { type H = f32; }

fn main() {
    // Create a trait object.
    let mut base: Box<Base<u32, H=f32>> = Box::new(Bar(42.0));

    // 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>());
}
```

## 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.