Add try_cast to cast any type to another.

This commit is contained in:
Stephen Chung 2020-05-07 19:16:50 +08:00
parent c607c7c428
commit 7f6ce29447

View File

@ -18,7 +18,7 @@ use crate::stdlib::{
any::{type_name, Any, TypeId}, any::{type_name, Any, TypeId},
boxed::Box, boxed::Box,
collections::HashMap, collections::HashMap,
fmt, fmt, mem, ptr,
string::String, string::String,
vec::Vec, vec::Vec,
}; };
@ -38,6 +38,9 @@ pub trait Variant: Any {
/// Convert this `Variant` trait object to `&mut dyn Any`. /// Convert this `Variant` trait object to `&mut dyn Any`.
fn as_mut_any(&mut self) -> &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<Self>) -> Box<dyn Any>;
/// Get the name of this type. /// Get the name of this type.
fn type_name(&self) -> &'static str; fn type_name(&self) -> &'static str;
@ -60,6 +63,9 @@ impl<T: Any + Clone> Variant for T {
fn as_mut_any(&mut self) -> &mut dyn Any { fn as_mut_any(&mut self) -> &mut dyn Any {
self as &mut dyn Any self as &mut dyn Any
} }
fn as_box_any(self: Box<Self>) -> Box<dyn Any> {
self as Box<dyn Any>
}
fn type_name(&self) -> &'static str { fn type_name(&self) -> &'static str {
type_name::<T>() type_name::<T>()
} }
@ -86,6 +92,9 @@ pub trait Variant: Any + Send + Sync {
/// Convert this `Variant` trait object to `&mut dyn Any`. /// Convert this `Variant` trait object to `&mut dyn Any`.
fn as_mut_any(&mut self) -> &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<dyn Any>;
/// Get the name of this type. /// Get the name of this type.
fn type_name(&self) -> &'static str; fn type_name(&self) -> &'static str;
@ -108,6 +117,9 @@ impl<T: Any + Clone + Send + Sync> Variant for T {
fn as_mut_any(&mut self) -> &mut dyn Any { fn as_mut_any(&mut self) -> &mut dyn Any {
self as &mut dyn Any self as &mut dyn Any
} }
fn as_box_any(self: Box<Self>) -> Box<dyn Any> {
self as Box<dyn Any>
}
fn type_name(&self) -> &'static str { fn type_name(&self) -> &'static str {
type_name::<T>() type_name::<T>()
} }
@ -284,6 +296,22 @@ impl Default for Dynamic {
} }
} }
/// Cast a type into another type.
fn try_cast<A: Any, B: Any>(a: A) -> Option<B> {
if TypeId::of::<B>() == 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. /// Cast a Boxed type into another type.
fn cast_box<X: Variant, T: Variant>(item: Box<X>) -> Result<Box<T>, Box<X>> { fn cast_box<X: Variant, T: Variant>(item: Box<X>) -> Result<Box<T>, Box<X>> {
// Only allow casting to the exact same type // Only allow casting to the exact same type
@ -388,26 +416,26 @@ impl Dynamic {
/// ///
/// assert_eq!(x.try_cast::<u32>().unwrap(), 42); /// assert_eq!(x.try_cast::<u32>().unwrap(), 42);
/// ``` /// ```
pub fn try_cast<T: Variant + Clone>(self) -> Option<T> { pub fn try_cast<T: Variant>(self) -> Option<T> {
if TypeId::of::<T>() == TypeId::of::<Dynamic>() { if TypeId::of::<T>() == TypeId::of::<Dynamic>() {
return cast_box::<_, T>(Box::new(self)).ok().map(|v| *v); return cast_box::<_, T>(Box::new(self)).ok().map(|v| *v);
} }
match self.0 { match self.0 {
Union::Unit(ref value) => (value as &dyn Any).downcast_ref::<T>().cloned(), Union::Unit(value) => try_cast(value),
Union::Bool(ref value) => (value as &dyn Any).downcast_ref::<T>().cloned(), Union::Bool(value) => try_cast(value),
Union::Str(value) => cast_box::<_, T>(value).ok().map(|v| *v), Union::Str(value) => cast_box::<_, T>(value).ok().map(|v| *v),
Union::Char(ref value) => (value as &dyn Any).downcast_ref::<T>().cloned(), Union::Char(value) => try_cast(value),
Union::Int(ref value) => (value as &dyn Any).downcast_ref::<T>().cloned(), Union::Int(value) => try_cast(value),
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
Union::Float(ref value) => (value as &dyn Any).downcast_ref::<T>().cloned(), Union::Float(value) => try_cast(value),
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Union::Array(value) => cast_box::<_, T>(value).ok().map(|v| *v), Union::Array(value) => cast_box::<_, T>(value).ok().map(|v| *v),
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Union::Map(value) => cast_box::<_, T>(value).ok().map(|v| *v), Union::Map(value) => cast_box::<_, T>(value).ok().map(|v| *v),
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Union::Module(value) => cast_box::<_, T>(value).ok().map(|v| *v), Union::Module(value) => cast_box::<_, T>(value).ok().map(|v| *v),
Union::Variant(value) => value.as_any().downcast_ref::<T>().cloned(), Union::Variant(value) => (*value).as_box_any().downcast().map(|x| *x).ok(),
} }
} }
@ -435,20 +463,20 @@ impl Dynamic {
} }
match self.0 { match self.0 {
Union::Unit(ref value) => (value as &dyn Any).downcast_ref::<T>().unwrap().clone(), Union::Unit(value) => try_cast(value).unwrap(),
Union::Bool(ref value) => (value as &dyn Any).downcast_ref::<T>().unwrap().clone(), Union::Bool(value) => try_cast(value).unwrap(),
Union::Str(value) => *cast_box::<_, T>(value).unwrap(), Union::Str(value) => *cast_box::<_, T>(value).unwrap(),
Union::Char(ref value) => (value as &dyn Any).downcast_ref::<T>().unwrap().clone(), Union::Char(value) => try_cast(value).unwrap(),
Union::Int(ref value) => (value as &dyn Any).downcast_ref::<T>().unwrap().clone(), Union::Int(value) => try_cast(value).unwrap(),
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
Union::Float(ref value) => (value as &dyn Any).downcast_ref::<T>().unwrap().clone(), Union::Float(value) => try_cast(value).unwrap(),
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Union::Array(value) => *cast_box::<_, T>(value).unwrap(), Union::Array(value) => *cast_box::<_, T>(value).unwrap(),
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Union::Map(value) => *cast_box::<_, T>(value).unwrap(), Union::Map(value) => *cast_box::<_, T>(value).unwrap(),
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Union::Module(value) => *cast_box::<_, T>(value).unwrap(), Union::Module(value) => *cast_box::<_, T>(value).unwrap(),
Union::Variant(value) => value.as_any().downcast_ref::<T>().unwrap().clone(), Union::Variant(value) => (*value).as_box_any().downcast().map(|x| *x).unwrap(),
} }
} }