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},
|
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(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user