Add try_cast to cast any type to another.
This commit is contained in:
parent
c607c7c428
commit
7f6ce29447
56
src/any.rs
56
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<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(),
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user