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},
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<Self>) -> Box<dyn Any>;
/// Get the name of this type.
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 {
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 {
type_name::<T>()
}
@ -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<dyn Any>;
/// Get the name of this type.
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 {
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 {
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.
fn cast_box<X: Variant, T: Variant>(item: Box<X>) -> Result<Box<T>, Box<X>> {
// Only allow casting to the exact same type
@ -388,26 +416,26 @@ impl Dynamic {
///
/// 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>() {
return cast_box::<_, T>(Box::new(self)).ok().map(|v| *v);
}
match self.0 {
Union::Unit(ref value) => (value as &dyn Any).downcast_ref::<T>().cloned(),
Union::Bool(ref value) => (value as &dyn Any).downcast_ref::<T>().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::<T>().cloned(),
Union::Int(ref value) => (value as &dyn Any).downcast_ref::<T>().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::<T>().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::<T>().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::<T>().unwrap().clone(),
Union::Bool(ref value) => (value as &dyn Any).downcast_ref::<T>().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::<T>().unwrap().clone(),
Union::Int(ref value) => (value as &dyn Any).downcast_ref::<T>().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::<T>().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::<T>().unwrap().clone(),
Union::Variant(value) => (*value).as_box_any().downcast().map(|x| *x).unwrap(),
}
}