use std::any::Any;
pub trait Downcast: Any {
fn as_any(&self) -> &Any;
fn as_any_mut(&mut self) -> &mut Any;
}
impl<T: Any> Downcast for T {
fn as_any(&self) -> &Any { self }
fn as_any_mut(&mut self) -> &mut Any { self }
}
#[macro_export]
macro_rules! impl_downcast {
(@$trait_:ident) => {
impl_downcast! {
@as_item
impl $trait_ {
impl_downcast! { @impl_body @$trait_ [] }
}
}
};
(@$trait_:ident [$($args:ident,)*]) => {
impl_downcast! {
@as_item
impl<$($args),*> $trait_<$($args),*>
where $( $args: ::std::any::Any + 'static ),*
{
impl_downcast! { @impl_body @$trait_ [$($args,)*] }
}
}
};
(@$trait_:ident [$($args:ident,)*] where [$($preds:tt)+]) => {
impl_downcast! {
@as_item
impl<$($args),*> $trait_<$($args),*>
where $( $args: ::std::any::Any + 'static, )*
$($preds)*
{
impl_downcast! { @impl_body @$trait_ [$($args,)*] }
}
}
};
(concrete @$trait_:ident [$($args:ident,)*]) => {
impl_downcast! {
@as_item
impl $trait_<$($args),*> {
impl_downcast! { @impl_body @$trait_ [$($args,)*] }
}
}
};
(@impl_body @$trait_:ident [$($args:ident,)*]) => {
#[inline]
pub fn is<__T: $trait_<$($args),*>>(&self) -> bool {
$crate::Downcast::as_any(self).is::<__T>()
}
#[inline]
pub fn downcast_ref<__T: $trait_<$($args),*>>(&self) -> Option<&__T> {
$crate::Downcast::as_any(self).downcast_ref::<__T>()
}
#[inline]
pub fn downcast_mut<__T: $trait_<$($args),*>>(&mut self) -> Option<&mut __T> {
$crate::Downcast::as_any_mut(self).downcast_mut::<__T>()
}
};
(@as_item $i:item) => { $i };
($trait_:ident <>) => { impl_downcast! { @$trait_ } };
($trait_:ident < $($args:ident),* $(,)* >) => { impl_downcast! { @$trait_ [$($args,)*] } };
($trait_:ident) => { impl_downcast! { @$trait_ } };
(concrete $trait_:ident < $($args:ident),* $(,)* >) => {
impl_downcast! { concrete @$trait_ [$($args,)*] }
};
($trait_:ident < $($args:ident),* $(,)* > where $($preds:tt)+) => {
impl_downcast! { @$trait_ [$($args,)*] where [$($preds)*] }
};
}
#[cfg(test)]
mod test {
mod non_generic {
use super::super::Downcast;
trait Base: Downcast {}
impl_downcast!(Base);
struct Foo(u32);
impl Base for Foo {}
fn get_val(base: &Box<Base>) -> u32 {
match base.downcast_ref::<Foo>() {
Some(val) => val.0,
None => 0
}
}
fn set_val(base: &mut Box<Base>, val: u32) {
if let Some(foo) = base.downcast_mut::<Foo>() {
foo.0 = val;
}
}
#[test]
fn test() {
let mut base: Box<Base> = Box::new(Foo(42));
assert_eq!(get_val(&base), 42);
set_val(&mut base, 6*9);
assert_eq!(get_val(&base), 6*9);
assert!(base.is::<Foo>());
}
}
mod generic {
use super::super::Downcast;
trait Base<T>: Downcast {}
impl_downcast!(Base<T>);
struct Foo(u32);
impl Base<u32> for Foo {}
fn get_val(base: &Box<Base<u32>>) -> u32 {
match base.downcast_ref::<Foo>() {
Some(val) => val.0,
None => 0
}
}
fn set_val(base: &mut Box<Base<u32>>, val: u32) {
if let Some(foo) = base.downcast_mut::<Foo>() {
foo.0 = val;
}
}
#[test]
fn test() {
let mut base: Box<Base<u32>> = Box::new(Foo(42));
assert_eq!(get_val(&base), 42);
set_val(&mut base, 6*9);
assert_eq!(get_val(&base), 6*9);
assert!(base.is::<Foo>());
}
}
mod constrained_generic {
use super::super::Downcast;
trait Base<T: Copy>: Downcast {}
impl_downcast!(Base<T> where T: Copy);
struct Foo(u32);
impl Base<u32> for Foo {}
fn get_val(base: &Box<Base<u32>>) -> u32 {
match base.downcast_ref::<Foo>() {
Some(val) => val.0,
None => 0
}
}
fn set_val(base: &mut Box<Base<u32>>, val: u32) {
if let Some(foo) = base.downcast_mut::<Foo>() {
foo.0 = val;
}
}
#[test]
fn test() {
let mut base: Box<Base<u32>> = Box::new(Foo(42));
assert_eq!(get_val(&base), 42);
set_val(&mut base, 6*9);
assert_eq!(get_val(&base), 6*9);
assert!(base.is::<Foo>());
}
}
mod concrete {
use super::super::Downcast;
trait Base<T>: Downcast {}
impl_downcast!(concrete Base<u32>);
struct Foo(u32);
impl Base<u32> for Foo {}
fn get_val(base: &Box<Base<u32>>) -> u32 {
match base.downcast_ref::<Foo>() {
Some(val) => val.0,
None => 0
}
}
fn set_val(base: &mut Box<Base<u32>>, val: u32) {
if let Some(foo) = base.downcast_mut::<Foo>() {
foo.0 = val;
}
}
#[test]
fn test() {
let mut base: Box<Base<u32>> = Box::new(Foo(42));
assert_eq!(get_val(&base), 42);
set_val(&mut base, 6*9);
assert_eq!(get_val(&base), 6*9);
assert!(base.is::<Foo>());
}
}
}