utils: add cast_* methods to allow more type-safe casting
Relying on just `as_any` was error prone and will become redundant in the future, once upcasting will be stable, we also won't to impose a restriction on to which concrete type we're casting, since casting to a type that doesn't implement a base trait doesn't make much sense. Co-authored-by: Kirill Chibisov <contact@kchibisov.com>
This commit is contained in:
parent
5cada36ae8
commit
16d5f46db1
19 changed files with 158 additions and 136 deletions
62
src/utils.rs
62
src/utils.rs
|
|
@ -28,19 +28,71 @@ impl<T> Deref for Lazy<T> {
|
|||
}
|
||||
}
|
||||
|
||||
pub trait AsAny {
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any;
|
||||
// NOTE: This is `pub`, but isn't actually exposed outside the crate.
|
||||
// NOTE: Marked as `#[doc(hidden)]` and underscored, because they can be quite difficult to use
|
||||
// correctly, see discussion in #4160.
|
||||
// FIXME: Remove and replace with a coercion once rust-lang/rust#65991 is in MSRV (1.86).
|
||||
#[doc(hidden)]
|
||||
pub trait AsAny: Any {
|
||||
#[doc(hidden)]
|
||||
fn __as_any(&self) -> &dyn Any;
|
||||
#[doc(hidden)]
|
||||
fn __as_any_mut(&mut self) -> &mut dyn Any;
|
||||
#[doc(hidden)]
|
||||
fn __into_any(self: Box<Self>) -> Box<dyn Any>;
|
||||
}
|
||||
|
||||
impl<T: Any> AsAny for T {
|
||||
#[inline(always)]
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
fn __as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
fn __as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn __into_any(self: Box<Self>) -> Box<dyn Any> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_dyn_casting {
|
||||
($trait:ident) => {
|
||||
impl dyn $trait + '_ {
|
||||
/// Downcast to the backend concrete type.
|
||||
///
|
||||
/// Returns `None` if the object was not from that backend.
|
||||
pub fn cast_ref<T: $trait>(&self) -> Option<&T> {
|
||||
let this: &dyn std::any::Any = self.__as_any();
|
||||
this.downcast_ref::<T>()
|
||||
}
|
||||
|
||||
/// Mutable downcast to the backend concrete type.
|
||||
///
|
||||
/// Returns `None` if the object was not from that backend.
|
||||
pub fn cast_mut<T: $trait>(&mut self) -> Option<&mut T> {
|
||||
let this: &mut dyn std::any::Any = self.__as_any_mut();
|
||||
this.downcast_mut::<T>()
|
||||
}
|
||||
|
||||
/// Owned downcast to the backend concrete type.
|
||||
///
|
||||
/// Returns `Err` with `self` if the object was not from that backend.
|
||||
pub fn cast<T: $trait>(self: Box<Self>) -> Result<Box<T>, Box<Self>> {
|
||||
let reference: &dyn std::any::Any = self.__as_any();
|
||||
if reference.is::<T>() {
|
||||
let this: Box<dyn std::any::Any> = self.__into_any();
|
||||
// Unwrap is okay, we just checked the type of `self` is `T`.
|
||||
Ok(this.downcast::<T>().unwrap())
|
||||
} else {
|
||||
Err(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub(crate) use impl_dyn_casting;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue