aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/lib.rs692
1 files changed, 692 insertions, 0 deletions
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..b211219
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,692 @@
+#![deny(unsafe_code)]
+#![cfg_attr(not(feature = "std"), no_std)]
+//! [![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.
+//!
+//! ```
+//! # #[macro_use]
+//! # extern crate downcast_rs;
+//! # use downcast_rs::{Downcast, DowncastSync};
+//! 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);
+//! # fn main() {}
+//! ```
+//!
+//! # Example without generics
+//!
+//! ```
+//! # use std::rc::Rc;
+//! # use std::sync::Arc;
+//! // 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
+//!
+//! ```
+//! // 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>());
+//! }
+//! ```
+
+// for compatibility with no std and macros
+#[doc(hidden)]
+#[cfg(not(feature = "std"))]
+pub extern crate core as __std;
+#[doc(hidden)]
+#[cfg(feature = "std")]
+pub extern crate std as __std;
+#[doc(hidden)]
+pub extern crate alloc as __alloc;
+
+use __std::any::Any;
+use __alloc::{boxed::Box, rc::Rc, sync::Arc};
+
+/// Supports conversion to `Any`. Traits to be extended by `impl_downcast!` must extend `Downcast`.
+pub trait Downcast: Any {
+ /// Convert `Box<dyn Trait>` (where `Trait: Downcast`) to `Box<dyn Any>`. `Box<dyn Any>` can
+ /// then be further `downcast` into `Box<ConcreteType>` where `ConcreteType` implements `Trait`.
+ fn into_any(self: Box<Self>) -> Box<dyn Any>;
+ /// Convert `Rc<Trait>` (where `Trait: Downcast`) to `Rc<Any>`. `Rc<Any>` can then be
+ /// further `downcast` into `Rc<ConcreteType>` where `ConcreteType` implements `Trait`.
+ fn into_any_rc(self: Rc<Self>) -> Rc<dyn Any>;
+ /// Convert `&Trait` (where `Trait: Downcast`) to `&Any`. This is needed since Rust cannot
+ /// generate `&Any`'s vtable from `&Trait`'s.
+ fn as_any(&self) -> &dyn Any;
+ /// Convert `&mut Trait` (where `Trait: Downcast`) to `&Any`. This is needed since Rust cannot
+ /// generate `&mut Any`'s vtable from `&mut Trait`'s.
+ fn as_any_mut(&mut self) -> &mut dyn Any;
+}
+
+impl<T: Any> Downcast for T {
+ fn into_any(self: Box<Self>) -> Box<dyn Any> { self }
+ fn into_any_rc(self: Rc<Self>) -> Rc<dyn Any> { self }
+ fn as_any(&self) -> &dyn Any { self }
+ fn as_any_mut(&mut self) -> &mut dyn Any { self }
+}
+
+/// Extends `Downcast` to support `Sync` traits that thus support `Arc` downcasting as well.
+pub trait DowncastSync: Downcast + Send + Sync {
+ /// Convert `Arc<Trait>` (where `Trait: Downcast`) to `Arc<Any>`. `Arc<Any>` can then be
+ /// further `downcast` into `Arc<ConcreteType>` where `ConcreteType` implements `Trait`.
+ fn into_any_arc(self: Arc<Self>) -> Arc<dyn Any + Send + Sync>;
+}
+
+impl<T: Any + Send + Sync> DowncastSync for T {
+ fn into_any_arc(self: Arc<Self>) -> Arc<dyn Any + Send + Sync> { self }
+}
+
+/// Adds downcasting support to traits that extend `downcast::Downcast` by defining forwarding
+/// methods to the corresponding implementations on `std::any::Any` in the standard library.
+///
+/// See https://users.rust-lang.org/t/how-to-create-a-macro-to-impl-a-provided-type-parametrized-trait/5289
+/// for why this is implemented this way to support templatized traits.
+#[macro_export(local_inner_macros)]
+macro_rules! impl_downcast {
+ (@impl_full
+ $trait_:ident [$($param_types:tt)*]
+ for [$($forall_types:ident),*]
+ where [$($preds:tt)*]
+ ) => {
+ impl_downcast! {
+ @inject_where
+ [impl<$($forall_types),*> dyn $trait_<$($param_types)*>]
+ types [$($forall_types),*]
+ where [$($preds)*]
+ [{
+ impl_downcast! { @impl_body $trait_ [$($param_types)*] }
+ }]
+ }
+ };
+
+ (@impl_full_sync
+ $trait_:ident [$($param_types:tt)*]
+ for [$($forall_types:ident),*]
+ where [$($preds:tt)*]
+ ) => {
+ impl_downcast! {
+ @inject_where
+ [impl<$($forall_types),*> dyn $trait_<$($param_types)*>]
+ types [$($forall_types),*]
+ where [$($preds)*]
+ [{
+ impl_downcast! { @impl_body $trait_ [$($param_types)*] }
+ impl_downcast! { @impl_body_sync $trait_ [$($param_types)*] }
+ }]
+ }
+ };
+
+ (@impl_body $trait_:ident [$($types:tt)*]) => {
+ /// Returns true if the trait object wraps an object of type `__T`.
+ #[inline]
+ pub fn is<__T: $trait_<$($types)*>>(&self) -> bool {
+ $crate::Downcast::as_any(self).is::<__T>()
+ }
+ /// Returns a boxed object from a boxed trait object if the underlying object is of type
+ /// `__T`. Returns the original boxed trait if it isn't.
+ #[inline]
+ pub fn downcast<__T: $trait_<$($types)*>>(
+ self: $crate::__alloc::boxed::Box<Self>
+ ) -> $crate::__std::result::Result<$crate::__alloc::boxed::Box<__T>, $crate::__alloc::boxed::Box<Self>> {
+ if self.is::<__T>() {
+ Ok($crate::Downcast::into_any(self).downcast::<__T>().unwrap())
+ } else {
+ Err(self)
+ }
+ }
+ /// Returns an `Rc`-ed object from an `Rc`-ed trait object if the underlying object is of
+ /// type `__T`. Returns the original `Rc`-ed trait if it isn't.
+ #[inline]
+ pub fn downcast_rc<__T: $trait_<$($types)*>>(
+ self: $crate::__alloc::rc::Rc<Self>
+ ) -> $crate::__std::result::Result<$crate::__alloc::rc::Rc<__T>, $crate::__alloc::rc::Rc<Self>> {
+ if self.is::<__T>() {
+ Ok($crate::Downcast::into_any_rc(self).downcast::<__T>().unwrap())
+ } else {
+ Err(self)
+ }
+ }
+ /// Returns a reference to the object within the trait object if it is of type `__T`, or
+ /// `None` if it isn't.
+ #[inline]
+ pub fn downcast_ref<__T: $trait_<$($types)*>>(&self) -> $crate::__std::option::Option<&__T> {
+ $crate::Downcast::as_any(self).downcast_ref::<__T>()
+ }
+ /// Returns a mutable reference to the object within the trait object if it is of type
+ /// `__T`, or `None` if it isn't.
+ #[inline]
+ pub fn downcast_mut<__T: $trait_<$($types)*>>(&mut self) -> $crate::__std::option::Option<&mut __T> {
+ $crate::Downcast::as_any_mut(self).downcast_mut::<__T>()
+ }
+ };
+
+ (@impl_body_sync $trait_:ident [$($types:tt)*]) => {
+ /// Returns an `Arc`-ed object from an `Arc`-ed trait object if the underlying object is of
+ /// type `__T`. Returns the original `Arc`-ed trait if it isn't.
+ #[inline]
+ pub fn downcast_arc<__T: $trait_<$($types)*>>(
+ self: $crate::__alloc::sync::Arc<Self>,
+ ) -> $crate::__std::result::Result<$crate::__alloc::sync::Arc<__T>, $crate::__alloc::sync::Arc<Self>>
+ where __T: $crate::__std::any::Any + $crate::__std::marker::Send + $crate::__std::marker::Sync
+ {
+ if self.is::<__T>() {
+ Ok($crate::DowncastSync::into_any_arc(self).downcast::<__T>().unwrap())
+ } else {
+ Err(self)
+ }
+ }
+ };
+
+ (@inject_where [$($before:tt)*] types [] where [] [$($after:tt)*]) => {
+ impl_downcast! { @as_item $($before)* $($after)* }
+ };
+
+ (@inject_where [$($before:tt)*] types [$($types:ident),*] where [] [$($after:tt)*]) => {
+ impl_downcast! {
+ @as_item
+ $($before)*
+ where $( $types: $crate::__std::any::Any + 'static ),*
+ $($after)*
+ }
+ };
+ (@inject_where [$($before:tt)*] types [$($types:ident),*] where [$($preds:tt)+] [$($after:tt)*]) => {
+ impl_downcast! {
+ @as_item
+ $($before)*
+ where
+ $( $types: $crate::__std::any::Any + 'static, )*
+ $($preds)*
+ $($after)*
+ }
+ };
+
+ (@as_item $i:item) => { $i };
+
+ // No type parameters.
+ ($trait_:ident ) => { impl_downcast! { @impl_full $trait_ [] for [] where [] } };
+ ($trait_:ident <>) => { impl_downcast! { @impl_full $trait_ [] for [] where [] } };
+ (sync $trait_:ident ) => { impl_downcast! { @impl_full_sync $trait_ [] for [] where [] } };
+ (sync $trait_:ident <>) => { impl_downcast! { @impl_full_sync $trait_ [] for [] where [] } };
+ // Type parameters.
+ ($trait_:ident < $($types:ident),* >) => {
+ impl_downcast! { @impl_full $trait_ [$($types),*] for [$($types),*] where [] }
+ };
+ (sync $trait_:ident < $($types:ident),* >) => {
+ impl_downcast! { @impl_full_sync $trait_ [$($types),*] for [$($types),*] where [] }
+ };
+ // Type parameters and where clauses.
+ ($trait_:ident < $($types:ident),* > where $($preds:tt)+) => {
+ impl_downcast! { @impl_full $trait_ [$($types),*] for [$($types),*] where [$($preds)*] }
+ };
+ (sync $trait_:ident < $($types:ident),* > where $($preds:tt)+) => {
+ impl_downcast! { @impl_full_sync $trait_ [$($types),*] for [$($types),*] where [$($preds)*] }
+ };
+ // Associated types.
+ ($trait_:ident assoc $($atypes:ident),*) => {
+ impl_downcast! { @impl_full $trait_ [$($atypes = $atypes),*] for [$($atypes),*] where [] }
+ };
+ (sync $trait_:ident assoc $($atypes:ident),*) => {
+ impl_downcast! { @impl_full_sync $trait_ [$($atypes = $atypes),*] for [$($atypes),*] where [] }
+ };
+ // Associated types and where clauses.
+ ($trait_:ident assoc $($atypes:ident),* where $($preds:tt)+) => {
+ impl_downcast! { @impl_full $trait_ [$($atypes = $atypes),*] for [$($atypes),*] where [$($preds)*] }
+ };
+ (sync $trait_:ident assoc $($atypes:ident),* where $($preds:tt)+) => {
+ impl_downcast! { @impl_full_sync $trait_ [$($atypes = $atypes),*] for [$($atypes),*] where [$($preds)*] }
+ };
+ // Type parameters and associated types.
+ ($trait_:ident < $($types:ident),* > assoc $($atypes:ident),*) => {
+ impl_downcast! {
+ @impl_full
+ $trait_ [$($types),*, $($atypes = $atypes),*]
+ for [$($types),*, $($atypes),*]
+ where []
+ }
+ };
+ (sync $trait_:ident < $($types:ident),* > assoc $($atypes:ident),*) => {
+ impl_downcast! {
+ @impl_full_sync
+ $trait_ [$($types),*, $($atypes = $atypes),*]
+ for [$($types),*, $($atypes),*]
+ where []
+ }
+ };
+ // Type parameters, associated types, and where clauses.
+ ($trait_:ident < $($types:ident),* > assoc $($atypes:ident),* where $($preds:tt)+) => {
+ impl_downcast! {
+ @impl_full
+ $trait_ [$($types),*, $($atypes = $atypes),*]
+ for [$($types),*, $($atypes),*]
+ where [$($preds)*]
+ }
+ };
+ (sync $trait_:ident < $($types:ident),* > assoc $($atypes:ident),* where $($preds:tt)+) => {
+ impl_downcast! {
+ @impl_full_sync
+ $trait_ [$($types),*, $($atypes = $atypes),*]
+ for [$($types),*, $($atypes),*]
+ where [$($preds)*]
+ }
+ };
+ // Concretely-parametrized types.
+ (concrete $trait_:ident < $($types:ident),* >) => {
+ impl_downcast! { @impl_full $trait_ [$($types),*] for [] where [] }
+ };
+ (sync concrete $trait_:ident < $($types:ident),* >) => {
+ impl_downcast! { @impl_full_sync $trait_ [$($types),*] for [] where [] }
+ };
+ // Concretely-associated types types.
+ (concrete $trait_:ident assoc $($atypes:ident = $aty:ty),*) => {
+ impl_downcast! { @impl_full $trait_ [$($atypes = $aty),*] for [] where [] }
+ };
+ (sync concrete $trait_:ident assoc $($atypes:ident = $aty:ty),*) => {
+ impl_downcast! { @impl_full_sync $trait_ [$($atypes = $aty),*] for [] where [] }
+ };
+ // Concretely-parametrized types with concrete associated types.
+ (concrete $trait_:ident < $($types:ident),* > assoc $($atypes:ident = $aty:ty),*) => {
+ impl_downcast! { @impl_full $trait_ [$($types),*, $($atypes = $aty),*] for [] where [] }
+ };
+ (sync concrete $trait_:ident < $($types:ident),* > assoc $($atypes:ident = $aty:ty),*) => {
+ impl_downcast! { @impl_full_sync $trait_ [$($types),*, $($atypes = $aty),*] for [] where [] }
+ };
+}
+
+
+#[cfg(test)]
+mod test {
+ macro_rules! test_mod {
+ (
+ $test_mod_name:ident,
+ trait $base_trait:path { $($base_impl:tt)* },
+ non_sync: { $($non_sync_def:tt)+ },
+ sync: { $($sync_def:tt)+ }
+ ) => {
+ test_mod! {
+ $test_mod_name,
+ trait $base_trait { $($base_impl:tt)* },
+ type dyn $base_trait,
+ non_sync: { $($non_sync_def)* },
+ sync: { $($sync_def)* }
+ }
+ };
+
+ (
+ $test_mod_name:ident,
+ trait $base_trait:path { $($base_impl:tt)* },
+ type $base_type:ty,
+ non_sync: { $($non_sync_def:tt)+ },
+ sync: { $($sync_def:tt)+ }
+ ) => {
+ mod $test_mod_name {
+ test_mod!(
+ @test
+ $test_mod_name,
+ test_name: test_non_sync,
+ trait $base_trait { $($base_impl)* },
+ type $base_type,
+ { $($non_sync_def)+ },
+ []);
+
+ test_mod!(
+ @test
+ $test_mod_name,
+ test_name: test_sync,
+ trait $base_trait { $($base_impl)* },
+ type $base_type,
+ { $($sync_def)+ },
+ [{
+ // Fail to convert Arc<Base> into Arc<Bar>.
+ let arc: $crate::__alloc::sync::Arc<$base_type> = $crate::__alloc::sync::Arc::new(Foo(42));
+ let res = arc.downcast_arc::<Bar>();
+ assert!(res.is_err());
+ let arc = res.unwrap_err();
+ // Convert Arc<Base> into Arc<Foo>.
+ assert_eq!(
+ 42, arc.downcast_arc::<Foo>().map_err(|_| "Shouldn't happen.").unwrap().0);
+ }]);
+ }
+ };
+
+ (
+ @test
+ $test_mod_name:ident,
+ test_name: $test_name:ident,
+ trait $base_trait:path { $($base_impl:tt)* },
+ type $base_type:ty,
+ { $($def:tt)+ },
+ [ $($more_tests:block)* ]
+ ) => {
+ #[test]
+ fn $test_name() {
+ #[allow(unused_imports)]
+ use super::super::{Downcast, DowncastSync};
+
+ // Should work even if standard objects (especially those in the prelude) are
+ // aliased to something else.
+ #[allow(dead_code)] struct Any;
+ #[allow(dead_code)] struct Arc;
+ #[allow(dead_code)] struct Box;
+ #[allow(dead_code)] struct Option;
+ #[allow(dead_code)] struct Result;
+ #[allow(dead_code)] struct Rc;
+ #[allow(dead_code)] struct Send;
+ #[allow(dead_code)] struct Sync;
+
+ // A trait that can be downcast.
+ $($def)*
+
+ // Concrete type implementing Base.
+ #[derive(Debug)]
+ struct Foo(u32);
+ impl $base_trait for Foo { $($base_impl)* }
+ #[derive(Debug)]
+ struct Bar(f64);
+ impl $base_trait for Bar { $($base_impl)* }
+
+ // Functions that can work on references to Base trait objects.
+ fn get_val(base: &$crate::__alloc::boxed::Box<$base_type>) -> u32 {
+ match base.downcast_ref::<Foo>() {
+ Some(val) => val.0,
+ None => 0
+ }
+ }
+ fn set_val(base: &mut $crate::__alloc::boxed::Box<$base_type>, val: u32) {
+ if let Some(foo) = base.downcast_mut::<Foo>() {
+ foo.0 = val;
+ }
+ }
+
+ let mut base: $crate::__alloc::boxed::Box<$base_type> = $crate::__alloc::boxed::Box::new(Foo(42));
+ assert_eq!(get_val(&base), 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);
+ }
+
+ set_val(&mut base, 6*9);
+ assert_eq!(get_val(&base), 6*9);
+
+ 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!(
+ 6*9, base.downcast::<Foo>().map_err(|_| "Shouldn't happen.").unwrap().0);
+
+ // Fail to convert Rc<Base> into Rc<Bar>.
+ let rc: $crate::__alloc::rc::Rc<$base_type> = $crate::__alloc::rc::Rc::new(Foo(42));
+ let res = rc.downcast_rc::<Bar>();
+ assert!(res.is_err());
+ let rc = res.unwrap_err();
+ // Convert Rc<Base> into Rc<Foo>.
+ assert_eq!(
+ 42, rc.downcast_rc::<Foo>().map_err(|_| "Shouldn't happen.").unwrap().0);
+
+ $($more_tests)*
+ }
+ };
+ (
+ $test_mod_name:ident,
+ trait $base_trait:path { $($base_impl:tt)* },
+ non_sync: { $($non_sync_def:tt)+ },
+ sync: { $($sync_def:tt)+ }
+ ) => {
+ test_mod! {
+ $test_mod_name,
+ trait $base_trait { $($base_impl:tt)* },
+ type $base_trait,
+ non_sync: { $($non_sync_def)* },
+ sync: { $($sync_def)* }
+ }
+ };
+
+ }
+
+ test_mod!(non_generic, trait Base {},
+ non_sync: {
+ trait Base: Downcast {}
+ impl_downcast!(Base);
+ },
+ sync: {
+ trait Base: DowncastSync {}
+ impl_downcast!(sync Base);
+ });
+
+ test_mod!(generic, trait Base<u32> {},
+ non_sync: {
+ trait Base<T>: Downcast {}
+ impl_downcast!(Base<T>);
+ },
+ sync: {
+ trait Base<T>: DowncastSync {}
+ impl_downcast!(sync Base<T>);
+ });
+
+ test_mod!(constrained_generic, trait Base<u32> {},
+ non_sync: {
+ trait Base<T: Copy>: Downcast {}
+ impl_downcast!(Base<T> where T: Copy);
+ },
+ sync: {
+ trait Base<T: Copy>: DowncastSync {}
+ impl_downcast!(sync Base<T> where T: Copy);
+ });
+
+ test_mod!(associated,
+ trait Base { type H = f32; },
+ type dyn Base<H=f32>,
+ non_sync: {
+ trait Base: Downcast { type H; }
+ impl_downcast!(Base assoc H);
+ },
+ sync: {
+ trait Base: DowncastSync { type H; }
+ impl_downcast!(sync Base assoc H);
+ });
+
+ test_mod!(constrained_associated,
+ trait Base { type H = f32; },
+ type dyn Base<H=f32>,
+ non_sync: {
+ trait Base: Downcast { type H: Copy; }
+ impl_downcast!(Base assoc H where H: Copy);
+ },
+ sync: {
+ trait Base: DowncastSync { type H: Copy; }
+ impl_downcast!(sync Base assoc H where H: Copy);
+ });
+
+ test_mod!(param_and_associated,
+ trait Base<u32> { type H = f32; },
+ type dyn Base<u32, H=f32>,
+ non_sync: {
+ trait Base<T>: Downcast { type H; }
+ impl_downcast!(Base<T> assoc H);
+ },
+ sync: {
+ trait Base<T>: DowncastSync { type H; }
+ impl_downcast!(sync Base<T> assoc H);
+ });
+
+ test_mod!(constrained_param_and_associated,
+ trait Base<u32> { type H = f32; },
+ type dyn Base<u32, H=f32>,
+ non_sync: {
+ trait Base<T: Clone>: Downcast { type H: Copy; }
+ impl_downcast!(Base<T> assoc H where T: Clone, H: Copy);
+ },
+ sync: {
+ trait Base<T: Clone>: DowncastSync { type H: Copy; }
+ impl_downcast!(sync Base<T> assoc H where T: Clone, H: Copy);
+ });
+
+ test_mod!(concrete_parametrized, trait Base<u32> {},
+ non_sync: {
+ trait Base<T>: Downcast {}
+ impl_downcast!(concrete Base<u32>);
+ },
+ sync: {
+ trait Base<T>: DowncastSync {}
+ impl_downcast!(sync concrete Base<u32>);
+ });
+
+ test_mod!(concrete_associated,
+ trait Base { type H = u32; },
+ type dyn Base<H=u32>,
+ non_sync: {
+ trait Base: Downcast { type H; }
+ impl_downcast!(concrete Base assoc H=u32);
+ },
+ sync: {
+ trait Base: DowncastSync { type H; }
+ impl_downcast!(sync concrete Base assoc H=u32);
+ });
+
+ test_mod!(concrete_parametrized_associated,
+ trait Base<u32> { type H = f32; },
+ type dyn Base<u32, H=f32>,
+ non_sync: {
+ trait Base<T>: Downcast { type H; }
+ impl_downcast!(concrete Base<u32> assoc H=f32);
+ },
+ sync: {
+ trait Base<T>: DowncastSync { type H; }
+ impl_downcast!(sync concrete Base<u32> assoc H=f32);
+ });
+}