aboutsummaryrefslogtreecommitdiff
path: root/README.md
diff options
context:
space:
mode:
Diffstat (limited to 'README.md')
-rw-r--r--README.md165
1 files changed, 165 insertions, 0 deletions
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..2c462f7
--- /dev/null
+++ b/README.md
@@ -0,0 +1,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.