commit | 244f18308666833f2b9171f189cee666cb6050d7 | [log] [tgz] |
---|---|---|
author | Xin Li <delphij@google.com> | Sat Feb 20 13:38:31 2021 +0000 |
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | Sat Feb 20 13:38:31 2021 +0000 |
tree | c295540a5003617f90d632937e11b9f81f2430d0 | |
parent | ba52f2e9052a3d78a92c2733dc77889de6c32376 [diff] | |
parent | c18fa2fdf06464f814bbf5289793376d6f2be7bc [diff] |
[automerger skipped] Mark ab/7061308 as merged in stage. am: 44618bfce5 -s ours am: c18fa2fdf0 -s ours am skip reason: Change-Id Icdb3e4856b29243a9e9e57c761934909ec79b41e with SHA-1 0d1c96b7e9 is in history Original change: undetermined MUST ONLY BE SUBMITTED BY AUTOMERGER Change-Id: I28225136f99fbf18515c07e0cd7c8d3e79a11be3
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.
Add the following to your Cargo.toml
:
[dependencies] downcast-rs = "1.2.0"
This crate is no_std
compatible. To use it without std
:
[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.
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);
// 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); }
// 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>()); }
Copyright 2020, Ashish Myles (maintainer) and contributors. This software is dual-licensed under the MIT and Apache 2.0 licenses.
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.