From 7f6ce29447de808c22caeacf84d8e33ccd36a668 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 7 May 2020 19:16:50 +0800 Subject: [PATCH] Add try_cast to cast any type to another. --- src/any.rs | 56 ++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 42 insertions(+), 14 deletions(-) diff --git a/src/any.rs b/src/any.rs index 16bb8ae7..156e8cba 100644 --- a/src/any.rs +++ b/src/any.rs @@ -18,7 +18,7 @@ use crate::stdlib::{ any::{type_name, Any, TypeId}, boxed::Box, collections::HashMap, - fmt, + fmt, mem, ptr, string::String, vec::Vec, }; @@ -38,6 +38,9 @@ pub trait Variant: Any { /// Convert this `Variant` trait object to `&mut dyn Any`. fn as_mut_any(&mut self) -> &mut dyn Any; + /// Convert this `Variant` trait object to an `Any` trait object. + fn as_box_any(self: Box) -> Box; + /// Get the name of this type. fn type_name(&self) -> &'static str; @@ -60,6 +63,9 @@ impl Variant for T { fn as_mut_any(&mut self) -> &mut dyn Any { self as &mut dyn Any } + fn as_box_any(self: Box) -> Box { + self as Box + } fn type_name(&self) -> &'static str { type_name::() } @@ -86,6 +92,9 @@ pub trait Variant: Any + Send + Sync { /// Convert this `Variant` trait object to `&mut dyn Any`. fn as_mut_any(&mut self) -> &mut dyn Any; + /// Convert this `Variant` trait object to an `Any` trait object. + fn as_box_any(self) -> Box; + /// Get the name of this type. fn type_name(&self) -> &'static str; @@ -108,6 +117,9 @@ impl Variant for T { fn as_mut_any(&mut self) -> &mut dyn Any { self as &mut dyn Any } + fn as_box_any(self: Box) -> Box { + self as Box + } fn type_name(&self) -> &'static str { type_name::() } @@ -284,6 +296,22 @@ impl Default for Dynamic { } } +/// Cast a type into another type. +fn try_cast(a: A) -> Option { + if TypeId::of::() == a.type_id() { + // SAFETY: Just checked we have the right type. We explicitly forget the + // value immediately after moving out, removing any chance of a destructor + // running or value otherwise being used again. + unsafe { + let ret: B = ptr::read(&a as *const _ as *const B); + mem::forget(a); + Some(ret) + } + } else { + None + } +} + /// Cast a Boxed type into another type. fn cast_box(item: Box) -> Result, Box> { // Only allow casting to the exact same type @@ -388,26 +416,26 @@ impl Dynamic { /// /// assert_eq!(x.try_cast::().unwrap(), 42); /// ``` - pub fn try_cast(self) -> Option { + pub fn try_cast(self) -> Option { if TypeId::of::() == TypeId::of::() { return cast_box::<_, T>(Box::new(self)).ok().map(|v| *v); } match self.0 { - Union::Unit(ref value) => (value as &dyn Any).downcast_ref::().cloned(), - Union::Bool(ref value) => (value as &dyn Any).downcast_ref::().cloned(), + Union::Unit(value) => try_cast(value), + Union::Bool(value) => try_cast(value), Union::Str(value) => cast_box::<_, T>(value).ok().map(|v| *v), - Union::Char(ref value) => (value as &dyn Any).downcast_ref::().cloned(), - Union::Int(ref value) => (value as &dyn Any).downcast_ref::().cloned(), + Union::Char(value) => try_cast(value), + Union::Int(value) => try_cast(value), #[cfg(not(feature = "no_float"))] - Union::Float(ref value) => (value as &dyn Any).downcast_ref::().cloned(), + Union::Float(value) => try_cast(value), #[cfg(not(feature = "no_index"))] Union::Array(value) => cast_box::<_, T>(value).ok().map(|v| *v), #[cfg(not(feature = "no_object"))] Union::Map(value) => cast_box::<_, T>(value).ok().map(|v| *v), #[cfg(not(feature = "no_module"))] Union::Module(value) => cast_box::<_, T>(value).ok().map(|v| *v), - Union::Variant(value) => value.as_any().downcast_ref::().cloned(), + Union::Variant(value) => (*value).as_box_any().downcast().map(|x| *x).ok(), } } @@ -435,20 +463,20 @@ impl Dynamic { } match self.0 { - Union::Unit(ref value) => (value as &dyn Any).downcast_ref::().unwrap().clone(), - Union::Bool(ref value) => (value as &dyn Any).downcast_ref::().unwrap().clone(), + Union::Unit(value) => try_cast(value).unwrap(), + Union::Bool(value) => try_cast(value).unwrap(), Union::Str(value) => *cast_box::<_, T>(value).unwrap(), - Union::Char(ref value) => (value as &dyn Any).downcast_ref::().unwrap().clone(), - Union::Int(ref value) => (value as &dyn Any).downcast_ref::().unwrap().clone(), + Union::Char(value) => try_cast(value).unwrap(), + Union::Int(value) => try_cast(value).unwrap(), #[cfg(not(feature = "no_float"))] - Union::Float(ref value) => (value as &dyn Any).downcast_ref::().unwrap().clone(), + Union::Float(value) => try_cast(value).unwrap(), #[cfg(not(feature = "no_index"))] Union::Array(value) => *cast_box::<_, T>(value).unwrap(), #[cfg(not(feature = "no_object"))] Union::Map(value) => *cast_box::<_, T>(value).unwrap(), #[cfg(not(feature = "no_module"))] Union::Module(value) => *cast_box::<_, T>(value).unwrap(), - Union::Variant(value) => value.as_any().downcast_ref::().unwrap().clone(), + Union::Variant(value) => (*value).as_box_any().downcast().map(|x| *x).unwrap(), } }