//! A library providing direct casting among trait objects implemented by a type. //! //! In Rust, an object of a sub-trait of [`Any`] can be downcast to a concrete type //! at runtime if the type is known. But no direct casting between two trait objects //! (i.e. without involving the concrete type of the backing value) is possible //! (even no coercion from a trait object to that of its super-trait yet). //! //! With this crate, any trait object with [`CastFrom`] as its super-trait can be cast //! directly to another trait object implemented by the underlying type if the target //! traits are registered beforehand with the macros provided by this crate. //! //! //! Originally from Intertrait by CodeChain //! //! //! //! //! Licensed under either of //! //! Apache License, Version 2.0 (LICENSE-APACHE or ) //! MIT license (LICENSE-MIT or ) //! //! at your option. use std::any::{Any, TypeId}; use std::rc::Rc; use std::sync::Arc; use ahash::AHashMap; use linkme::distributed_slice; use once_cell::sync::Lazy; pub mod cast; pub type BoxedCaster = Box; /// A distributed slice gathering constructor functions for [`Caster`]s. /// /// A constructor function returns `TypeId` of a concrete type involved in the casting /// and a `Box` of a trait object backed by a [`Caster`]. /// /// [`Caster`]: ./struct.Caster.html #[distributed_slice] pub static CASTERS: [fn() -> (TypeId, BoxedCaster)] = [..]; /// A `HashMap` mapping `TypeId` of a [`Caster`] to an instance of it. /// /// [`Caster`]: ./struct.Caster.html static CASTER_MAP: Lazy> = Lazy::new(|| { CASTERS .iter() .map(|caster_fn| { let (type_id, caster) = caster_fn(); ((type_id, (*caster).type_id()), caster) }) .collect() }); type CastArcFn = fn(from: Arc) -> Arc; /// A `Caster` knows how to cast a reference to or `Box` of a trait object for `Any` /// to a trait object of trait `Trait`. Each `Caster` instance is specific to a concrete /// type. That is, it knows how to cast to single specific trait implemented by single /// specific type. pub struct Caster { /// Casts a `Box` holding a trait object for `Any` to another `Box` holding a trait /// object for trait `Trait`. pub cast_box: fn(from: Box) -> Box, /// Casts an `Rc` holding a trait object for `Any` to another `Rc` holding a trait /// object for trait `Trait`. pub cast_rc: fn(from: Rc) -> Rc, /// Casts an `Arc` holding a trait object for `Any + Sync + Send + 'static` /// to another `Arc` holding a trait object for trait `Trait`. pub opt_cast_arc: Option>, } impl Caster { pub fn new( cast_box: fn(from: Box) -> Box, cast_rc: fn(from: Rc) -> Rc, ) -> Caster { Caster:: { cast_box, cast_rc, opt_cast_arc: None, } } #[allow(clippy::similar_names)] pub fn new_sync( cast_box: fn(from: Box) -> Box, cast_rc: fn(from: Rc) -> Rc, cast_arc: fn(from: Arc) -> Arc, ) -> Caster { Caster:: { cast_box, cast_rc, opt_cast_arc: Some(cast_arc), } } } /// Returns a `Caster` from a concrete type `S` to a trait `Trait` implemented /// by it. fn get_caster(type_id: TypeId) -> Option<&'static Caster> { CASTER_MAP .get(&(type_id, TypeId::of::>())) .and_then(|caster| caster.downcast_ref::>()) } /// `CastFrom` must be extended by a trait that wants to allow for casting into another /// trait. /// /// It is used for obtaining a trait object for [`Any`] from a trait object for its /// sub-trait, and blanket implemented for all `Sized + Any + 'static` types. /// /// # Examples /// ```ignore /// trait Source: CastFrom { /// ... /// } /// ``` pub trait CastFrom: Any + 'static { /// Returns a `Box` of `Any`, which is backed by the type implementing this trait. fn box_any(self: Box) -> Box; /// Returns an `Rc` of `Any`, which is backed by the type implementing this trait. fn rc_any(self: Rc) -> Rc; } /// `CastFromSync` must be extended by a trait that is `Any + Sync + Send + 'static` /// and wants to allow for casting into another trait behind references and smart pointers /// especially including `Arc`. /// /// It is used for obtaining a trait object for [`Any + Sync + Send + 'static`] from an /// object for its sub-trait, and blanket implemented for all `Sized + Sync + Send + /// 'static` types. /// /// # Examples /// ```ignore /// trait Source: CastFromSync { /// ... /// } /// ``` pub trait CastFromSync: CastFrom + Sync + Send + 'static { fn arc_any(self: Arc) -> Arc; } impl CastFrom for Trait { fn box_any(self: Box) -> Box { self } fn rc_any(self: Rc) -> Rc { self } } impl CastFrom for dyn Any + 'static { fn box_any(self: Box) -> Box { self } fn rc_any(self: Rc) -> Rc { self } } impl CastFromSync for Trait { fn arc_any(self: Arc) -> Arc { self } } impl CastFrom for dyn Any + Sync + Send + 'static { fn box_any(self: Box) -> Box { self } fn rc_any(self: Rc) -> Rc { self } } impl CastFromSync for dyn Any + Sync + Send + 'static { fn arc_any(self: Arc) -> Arc { self } } #[cfg(test)] mod tests { use std::any::{Any, TypeId}; use std::fmt::{Debug, Display}; use linkme::distributed_slice; #[allow(clippy::wildcard_imports)] use super::cast::*; use super::*; #[distributed_slice(super::CASTERS)] static TEST_CASTER: fn() -> (TypeId, BoxedCaster) = create_test_caster; #[derive(Debug)] struct TestStruct; trait SourceTrait: CastFromSync {} impl SourceTrait for TestStruct {} fn create_test_caster() -> (TypeId, BoxedCaster) { let type_id = TypeId::of::(); let caster = Box::new(Caster:: { cast_box: |from| from.downcast::().unwrap(), cast_rc: |from| from.downcast::().unwrap(), opt_cast_arc: Some(|from| from.downcast::().unwrap()), }); (type_id, caster) } #[test] fn cast_box() { let ts = Box::new(TestStruct); let st: Box = ts; let debug = st.cast::(); assert!(debug.is_ok()); } #[test] fn cast_rc() { let ts = Rc::new(TestStruct); let st: Rc = ts; let debug = st.cast::(); assert!(debug.is_ok()); } #[test] fn cast_arc() { let ts = Arc::new(TestStruct); let st: Arc = ts; let debug = st.cast::(); assert!(debug.is_ok()); } #[test] fn cast_box_wrong() { let ts = Box::new(TestStruct); let st: Box = ts; let display = st.cast::(); assert!(display.is_err()); } #[test] fn cast_rc_wrong() { let ts = Rc::new(TestStruct); let st: Rc = ts; let display = st.cast::(); assert!(display.is_err()); } #[test] fn cast_arc_wrong() { let ts = Arc::new(TestStruct); let st: Arc = ts; let display = st.cast::(); assert!(display.is_err()); } #[test] fn cast_box_from_any() { let ts = Box::new(TestStruct); let st: Box = ts; let debug = st.cast::(); assert!(debug.is_ok()); } #[test] fn cast_rc_from_any() { let ts = Rc::new(TestStruct); let st: Rc = ts; let debug = st.cast::(); assert!(debug.is_ok()); } #[test] fn cast_arc_from_any() { let ts = Arc::new(TestStruct); let st: Arc = ts; let debug = st.cast::(); assert!(debug.is_ok()); } }