├── .gitignore ├── Cargo.toml ├── README.md ├── core ├── .gitignore ├── Cargo.toml ├── LICENSE └── src │ ├── inventory.rs │ ├── lib.rs │ └── tests.rs ├── rustfmt.toml └── traitcast ├── .gitignore ├── Cargo.toml ├── LICENSE └── src ├── lib.rs └── tests.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "core", 4 | "traitcast", 5 | ] 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Traitcast 2 | --------- 3 | 4 | ## Casting from `Any` 5 | 6 | In the standard library, the std::any::Any trait comes with downcast methods 7 | which let you cast from an `Any` trait object to a concrete type. 8 | 9 | ```rust 10 | let x: i32 = 7; 11 | let y: &dyn std::any::Any = &x; 12 | 13 | // Cast to i32 succeeds because x: i32 14 | assert_eq!(y.downcast_ref::(), Some(&7)); 15 | // Cast to f32 fails 16 | assert_eq!(y.downcast_ref::(), None); 17 | ``` 18 | 19 | However, it is not possible to downcast to a trait object. 20 | 21 | ```compile_fail 22 | trait Foo { 23 | fn foo(&self) -> i32; 24 | } 25 | 26 | struct A { 27 | x: i32 28 | } 29 | 30 | impl Foo for A { 31 | fn foo(&self) -> i32 { 32 | self.x 33 | } 34 | } 35 | 36 | let x = A { x: 7 }; 37 | let y: &dyn std::any::Any = &x; 38 | 39 | // This cast is not possible, because it is only possible to cast to types that 40 | // are Sized. Among other things, this precludes trait objects. 41 | let z: Option<&dyn Foo> = y.downcast_ref(); 42 | ``` 43 | 44 | ## Traitcast 45 | 46 | This library provides a way of casting between different trait objects. 47 | 48 | ```rust 49 | use traitcast::{TraitcastFrom, Traitcast}; 50 | 51 | // Extending `TraitcastFrom` is optional. This allows `Foo` objects themselves 52 | // to be cast to other trait objects. If you do not extend `TraitcastFrom`, 53 | // then Foo may only be cast into, not out of. 54 | trait Foo: TraitcastFrom { 55 | fn foo(&self) -> i32; 56 | } 57 | 58 | trait Bar: TraitcastFrom { 59 | fn bar(&mut self) -> i32; 60 | } 61 | 62 | struct A { 63 | x: i32 64 | } 65 | 66 | // No implementation of TraitcastFrom is necessary, because it is covered by 67 | // the blanket impl for any sized type with a static lifetime. 68 | impl Foo for A { 69 | fn foo(&self) -> i32 { 70 | self.x 71 | } 72 | } 73 | 74 | impl Bar for A { 75 | fn bar(&mut self) -> i32 { 76 | self.x *= 2; 77 | self.x 78 | } 79 | } 80 | 81 | // Register the traits. 82 | 83 | // For each struct that implements each trait, register the implementation. 84 | traitcast::traitcast!(struct A: Foo, Bar); 85 | 86 | fn main() { 87 | let mut x = A { x: 7 }; 88 | 89 | { 90 | let x: &dyn Foo = &x; 91 | // Test whether x is of a type that implements Bar. 92 | assert!(traitcast::implements_trait::(x)); 93 | } 94 | 95 | { 96 | let x: &dyn Bar = &x; 97 | // Cast an immutable reference using the `cast_ref` method (via the 98 | // `Traitcast` trait, which is blanket implemented for all pairs of 99 | // traits that may be cast between). 100 | let x: &dyn Foo = x.cast_ref().unwrap(); 101 | assert_eq!(x.foo(), 7); 102 | 103 | // We can also cast using the top-level `cast_ref` function, which can 104 | // be more convenient when type arguments cannot be inferred. 105 | assert!(traitcast::cast_ref::(x).is_some()); 106 | } 107 | 108 | { 109 | let x: &mut dyn Foo = &mut x; 110 | // Cast a mutable reference using the `cast_mut` method 111 | let x: &mut dyn Bar = x.cast_mut().unwrap(); 112 | assert_eq!(x.bar(), 14); 113 | } 114 | 115 | { 116 | // We can cast from `Any` too! 117 | let y: Box = Box::new(x); 118 | // Cast a boxed reference 119 | let z: Box = y.cast_box().unwrap(); 120 | assert_eq!(z.foo(), 14); 121 | } 122 | } 123 | ``` 124 | 125 | -------------------------------------------------------------------------------- /core/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /Cargo.lock 3 | -------------------------------------------------------------------------------- /core/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "traitcast_core" 3 | version = "0.2.0" 4 | authors = ["Bradley Hardy "] 5 | edition = "2018" 6 | license = "MIT" 7 | description = "Cast between dynamic trait objects." 8 | repository = "https://github.com/bch29/traitcast" 9 | 10 | [dependencies] 11 | anymap = "0.12.*" 12 | inventory = { version = "0.1.*", optional = true } 13 | 14 | [dev-dependencies] 15 | inventory = "0.1.*" 16 | 17 | [features] 18 | use_inventory = ["inventory"] 19 | -------------------------------------------------------------------------------- /core/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2019 Bradley Hardy 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /core/src/inventory.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | This module defines helper types for using `traitcast` along with the 3 | `inventory` crate. Requires the `use_inventory` feature. 4 | */ 5 | use crate::{CastIntoTrait, ImplEntry, Registry}; 6 | 7 | /// Makes a trait registry by collecting EntryBuilders with the `inventory` 8 | /// crate. 9 | pub fn build_registry() -> Registry { 10 | let mut reg = Registry::new(); 11 | for builder in inventory::iter:: { 12 | (builder.insert)(&mut reg); 13 | } 14 | reg 15 | } 16 | 17 | /// This is instantiated once for each castable trait. It describes how a trait 18 | /// can insert itself into the global table. 19 | pub struct EntryBuilder { 20 | pub insert: Box, 21 | } 22 | 23 | impl EntryBuilder { 24 | /// Constructs a EntryBuilder for trait `To` by collecting ImplEntry from 25 | /// `inventory`. If the table for `To` exists already, overwrites it. 26 | pub fn collecting_entries() -> EntryBuilder 27 | where 28 | Entry: inventory::Collect + AsRef>, 29 | To: 'static + ?Sized, 30 | { 31 | use std::iter::FromIterator; 32 | EntryBuilder { 33 | insert: Box::new(|master| { 34 | master.insert(CastIntoTrait::from_iter( 35 | inventory::iter:: 36 | .into_iter() 37 | .map(|x| x.as_ref().clone()), 38 | )) 39 | }), 40 | } 41 | } 42 | 43 | /// Constructs a trait builder that enters a single 'from' entry into the 44 | /// table for a particular target. 45 | /// 46 | /// If the does not exist already, creates a new table. If it exists 47 | /// already, modifies the existing table by inserting the new entry. 48 | pub fn inserting_entry(entry: ImplEntry) -> EntryBuilder 49 | where 50 | To: 'static + ?Sized, 51 | { 52 | EntryBuilder { 53 | insert: Box::new(move |master| { 54 | let table: &mut CastIntoTrait = 55 | master.tables 56 | .entry::>() 57 | .or_insert(CastIntoTrait::new()); 58 | 59 | table.map.insert(entry.tid, entry.clone()); 60 | }) 61 | } 62 | } 63 | } 64 | 65 | inventory::collect!(EntryBuilder); 66 | 67 | /// Macro for registering traitcast entries with inventory. Requires the 68 | /// "use_inventory" feature. 69 | /// 70 | /// `traitcast!(struct Bar)` registers a struct to allow it to be cast into. 71 | /// 72 | /// `traitcast!(impl Foo for Bar)` allows casting into dynamic `Foo` trait 73 | /// objects, from objects whose concrete type is `Bar`. 74 | /// 75 | /// `traitcast!(struct Bar: Foo1, Foo2)` registers a struct to allow it to be 76 | /// cast into, and further allows casting into dynamic `Foo1` or `Foo2` trait 77 | /// objects, from objects whose concrete type is `Bar`. 78 | #[cfg(feature = "use_inventory")] 79 | #[macro_export] 80 | macro_rules! traitcast { 81 | (struct $type:ty) => { 82 | $crate::traitcast!($type => $type); 83 | }; 84 | (struct $type:ty : $($trait:path),+) => { 85 | $crate::traitcast!(struct $type); 86 | $( 87 | $crate::traitcast!(impl $trait; for $type); 88 | )+ 89 | }; 90 | (impl $trait:path; for $source:ty) => { 91 | $crate::traitcast!($source => dyn $trait); 92 | }; 93 | ($source:ty => $target:ty) => { 94 | inventory::submit! { 95 | $crate::inventory::EntryBuilder::inserting_entry( 96 | $crate::impl_entry!($target, $source)) 97 | } 98 | }; 99 | } 100 | -------------------------------------------------------------------------------- /core/src/lib.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | This module is a general interface to `traitcast` which does not rely on a 3 | global registry. This makes it more flexible at the cost of having to create 4 | a registry and pass it around. If you do not want to do that, use the root 5 | `traitcast` module which provides a convenient global registry. 6 | */ 7 | 8 | #[cfg(feature = "use_inventory")] 9 | pub mod inventory; 10 | 11 | // #[cfg(test)] 12 | // pub mod tests; 13 | 14 | use std::any::{Any, TypeId}; 15 | use std::collections::HashMap; 16 | 17 | /// A registry defining how to cast into some set of traits. 18 | pub struct Registry { 19 | pub tables: anymap::Map, 20 | } 21 | 22 | impl Registry { 23 | /// Makes a new, empty trait registry. 24 | pub fn new() -> Registry { 25 | Registry { 26 | tables: anymap::Map::new(), 27 | } 28 | } 29 | 30 | /// Updates the table defining how to cast into the given trait. 31 | pub fn insert( 32 | &mut self, 33 | table: CastIntoTrait, 34 | ) { 35 | self.tables.insert(table); 36 | } 37 | 38 | /// Gets the table defining how to cast into the given trait. 39 | /// 40 | /// This method is designed to be chained with from_mut, from_ref or 41 | /// from_box. 42 | /// 43 | /// # Examples 44 | /// ```text 45 | /// let x: &dyn Bar = ...; 46 | /// registry.cast_into::()?.from_ref(x) 47 | /// 48 | /// let x: &mut dyn Bar = ...; 49 | /// registry.cast_into::()?.from_mut(x) 50 | /// 51 | /// let x: Box = ...; 52 | /// registry.cast_into::()?.from_box(x) 53 | /// ``` 54 | pub fn cast_into(&self) -> Option<&CastIntoTrait> 55 | where 56 | To: ?Sized + 'static, 57 | { 58 | self.tables.get::>() 59 | } 60 | } 61 | 62 | /// Provides methods for casting into the target trait object from other trait 63 | /// objects. 64 | pub struct CastIntoTrait { 65 | pub map: HashMap>, 66 | } 67 | 68 | impl CastIntoTrait { 69 | pub fn new() -> Self { 70 | CastIntoTrait { map: HashMap::new() } 71 | } 72 | } 73 | 74 | impl std::iter::FromIterator> 75 | for CastIntoTrait 76 | { 77 | fn from_iter(iter: T) -> Self 78 | where 79 | T: IntoIterator>, 80 | { 81 | CastIntoTrait { 82 | map: iter.into_iter().map(|x| (x.tid, x)).collect(), 83 | } 84 | } 85 | } 86 | 87 | impl CastIntoTrait { 88 | /// Tries to cast the given reference to a dynamic trait object. This will 89 | /// always return None if the implementation of the target trait, for the 90 | /// concrete type of x, has not been registered via `traitcast_to_impl!`. 91 | pub fn from_ref<'a, From>(&self, x: &'a From) -> Option<&'a To> 92 | where 93 | From: TraitcastFrom + ?Sized, 94 | { 95 | let x = (*x).as_any_ref(); 96 | let tid = x.type_id(); 97 | let s = self.map.get(&tid)?; 98 | (s.cast_ref)(x) 99 | } 100 | 101 | /// Tries to cast the given mutable reference to a dynamic trait object. 102 | /// This will always return None if the implementation of the target trait, 103 | /// for the concrete type of x, has not been registered via 104 | /// `traitcast_to_impl!`. 105 | pub fn from_mut<'a, From>(&self, x: &'a mut From) -> Option<&'a mut To> 106 | where 107 | From: TraitcastFrom + ?Sized, 108 | { 109 | let x = (*x).as_any_mut(); 110 | let tid = (x as &dyn Any).type_id(); 111 | let s = self.map.get(&tid)?; 112 | (s.cast_mut)(x) 113 | } 114 | 115 | /// Tries to cast the given pointer to a dynamic trait object. This will 116 | /// always return Err if the implementation of the target trait, for the 117 | /// concrete type of x, has not been registered via `traitcast_to_impl!`. 118 | pub fn from_box(&self, x: Box) -> Result, Box> 119 | where 120 | From: TraitcastFrom + ?Sized, 121 | { 122 | let x = x.as_any_box(); 123 | 124 | // Must ensure we take the type id of what's in the box, not the type 125 | // id of the box itself. 126 | let tid = (*x).type_id(); 127 | 128 | let s = match self.map.get(&tid) { 129 | Some(s) => s, 130 | None => return Err(x), 131 | }; 132 | 133 | (s.cast_box)(x) 134 | } 135 | } 136 | 137 | /// An entry in the table for a particular castable trait. Stores methods to 138 | /// cast into one particular struct that implements the trait. 139 | pub struct ImplEntry { 140 | pub cast_box: fn(Box) -> Result, Box>, 141 | pub cast_mut: fn(&mut dyn Any) -> Option<&mut DynTrait>, 142 | pub cast_ref: fn(&dyn Any) -> Option<&DynTrait>, 143 | pub tid: TypeId, 144 | pub from_name: &'static str, 145 | pub into_name: &'static str 146 | } 147 | 148 | /// Manual `Clone` impl to allow for unsized T. 149 | impl Clone for ImplEntry { 150 | fn clone(&self) -> Self { 151 | ImplEntry { 152 | cast_box: self.cast_box, 153 | cast_mut: self.cast_mut, 154 | cast_ref: self.cast_ref, 155 | tid: self.tid, 156 | from_name: self.from_name, 157 | into_name: self.into_name 158 | } 159 | } 160 | } 161 | 162 | /// Subtraits of `TraitcastFrom` may be cast into `dyn Any`, and thus may be 163 | /// cast into any other castable dynamic trait object, too. This is blanket 164 | /// implemented for all sized types with static lifetimes. 165 | pub trait TraitcastFrom { 166 | /// Cast to an immutable reference to a trait object. 167 | fn as_any_ref(&self) -> &dyn Any; 168 | 169 | /// Cast to a mutable reference to a trait object. 170 | fn as_any_mut(&mut self) -> &mut dyn Any; 171 | 172 | /// Cast to a boxed reference to a trait object. 173 | fn as_any_box(self: Box) -> Box; 174 | 175 | /// Get the trait object's dynamic type id. 176 | fn type_id(&self) -> std::any::TypeId { 177 | self.as_any_ref().type_id() 178 | } 179 | } 180 | 181 | /// Blanket implementation that automatically implements TraitcastFrom for most 182 | /// user-defined types. 183 | impl TraitcastFrom for T 184 | where 185 | T: Sized + 'static, 186 | { 187 | fn as_any_ref(&self) -> &dyn Any { 188 | self 189 | } 190 | 191 | fn as_any_mut(&mut self) -> &mut dyn Any { 192 | self 193 | } 194 | 195 | fn as_any_box(self: Box) -> Box { 196 | self 197 | } 198 | } 199 | 200 | impl TraitcastFrom for dyn Any { 201 | fn as_any_ref(&self) -> &dyn Any { 202 | self 203 | } 204 | 205 | fn as_any_mut(&mut self) -> &mut dyn Any { 206 | self 207 | } 208 | 209 | fn as_any_box(self: Box) -> Box { 210 | self 211 | } 212 | } 213 | 214 | /// Constructs a `ImplEntry` for a trait and a concrete struct implementing 215 | /// that trait. 216 | /// 217 | /// # Example 218 | /// ``` 219 | /// # use traitcast_core::impl_entry; 220 | /// # use traitcast_core::ImplEntry; 221 | /// use std::fmt::Display; 222 | /// let x: ImplEntry = impl_entry!(dyn Display, i32); 223 | /// ``` 224 | #[macro_export] 225 | macro_rules! impl_entry { 226 | ($source:ty, $target:ty) => { 227 | $crate::ImplEntry::<$source> { 228 | cast_box: |x| { 229 | let x: Box<$target> = x.downcast()?; 230 | let x: Box<$source> = x; 231 | Ok(x) 232 | }, 233 | cast_mut: |x| { 234 | let x: &mut $target = x.downcast_mut()?; 235 | let x: &mut $source = x; 236 | Some(x) 237 | }, 238 | cast_ref: |x| { 239 | let x: &$target = x.downcast_ref()?; 240 | let x: &$source = x; 241 | Some(x) 242 | }, 243 | tid: std::any::TypeId::of::<$target>(), 244 | from_name: stringify!($source), 245 | into_name: stringify!($target) 246 | } 247 | }; 248 | } 249 | 250 | /// Creates a struct named `$wrapper` which wraps `ImplEntry` for 251 | /// the given `$trait`. This is useful because it allows implementing traits on 252 | /// the `ImplEntry` from external modules. This is an 253 | /// implementation detail of `traitcast_to_trait!`. 254 | #[macro_export] 255 | macro_rules! defn_impl_entry_wrapper { 256 | ($type:ty, $vis:vis $wrapper:ident) => { 257 | #[allow(non_camel_case_types)] 258 | $vis struct $wrapper(pub $crate::ImplEntry<$type>); 259 | 260 | impl std::convert::From<$crate::ImplEntry<$type>> for $wrapper { 261 | fn from(x: $crate::ImplEntry<$type>) -> Self { 262 | $wrapper(x) 263 | } 264 | } 265 | 266 | impl std::convert::AsRef<$crate::ImplEntry<$type>> for $wrapper { 267 | fn as_ref(&self) -> &$crate::ImplEntry<$type> { 268 | &self.0 269 | } 270 | } 271 | }; 272 | } 273 | 274 | -------------------------------------------------------------------------------- /core/src/tests.rs: -------------------------------------------------------------------------------- 1 | // #![cfg(test)] 2 | 3 | // use std::any::Any; 4 | 5 | // mod traits { 6 | // pub trait Foo: crate::TraitcastFrom { 7 | // fn foo(&mut self) -> i64; 8 | // } 9 | 10 | // pub trait Bar: crate::TraitcastFrom { 11 | // fn bar(&self) -> i64; 12 | // } 13 | 14 | // pub trait Baz: crate::TraitcastFrom { 15 | // fn baz(self: Box) -> i64; 16 | // } 17 | 18 | // crate::traitcast_to_trait!(Foo, Foo_Traitcast); 19 | // crate::traitcast_to_trait!(Bar, Bar_Traitcast); 20 | // crate::traitcast_to_trait!(Baz, Baz_Traitcast); 21 | // } 22 | 23 | // mod structs { 24 | // use crate::tests::traits::{Foo, Bar, Baz}; 25 | // pub struct A { 26 | // pub x: i64 27 | // } 28 | 29 | // pub struct B { 30 | // pub y: i64 31 | // } 32 | 33 | // impl Foo for A { 34 | // fn foo(&mut self) -> i64 { 35 | // self.x += 1; 36 | // self.x 37 | // } 38 | // } 39 | 40 | // impl Bar for A { 41 | // fn bar(&self) -> i64 { 42 | // self.x 43 | // } 44 | // } 45 | 46 | // impl Foo for B { 47 | // fn foo(&mut self) -> i64 { 48 | // self.y *= 2; 49 | // self.y 50 | // } 51 | // } 52 | 53 | // impl Baz for B { 54 | // fn baz(self: Box) -> i64 { 55 | // self.y 56 | // } 57 | // } 58 | 59 | // crate::traitcast_to_impl!(Foo, A); 60 | // crate::traitcast_to_impl!(Bar, A); 61 | // crate::traitcast_to_impl!(Foo, B); 62 | // crate::traitcast_to_impl!(Baz, B); 63 | // } 64 | 65 | // use traits::*; 66 | // use structs::*; 67 | 68 | // use crate::Traitcast; 69 | 70 | // #[test] 71 | // fn test_traitcast() { 72 | // let mut x: Box = Box::new(A { x: 0 }); 73 | // let mut y: Box = Box::new(B { y: 1 }); 74 | 75 | // { 76 | // // Can cast from Any to Bar 77 | // let x: &dyn Bar = (*x).cast_ref().unwrap(); 78 | // assert_eq!(x.bar(), 0); 79 | 80 | // // Can cast from Bar to Foo 81 | // assert!(crate::cast_ref::(x).is_some()); 82 | 83 | // // Any to Bar cast fails when the type does not implement Bar 84 | // assert!(crate::cast_ref::(&*y).is_none()); 85 | // } 86 | 87 | // { 88 | // // Can cast from Any to Foo 89 | // let x: &mut dyn Foo = (*x).cast_mut().unwrap(); 90 | // assert_eq!(x.foo(), 1); 91 | // assert_eq!(x.foo(), 2); 92 | 93 | // // Can cast from Foo to Bar 94 | // let x: &mut dyn Bar = x.cast_mut().unwrap(); 95 | // assert_eq!(x.bar(), 2); 96 | 97 | // // Can also cast B from Any to Foo 98 | // let y: &mut dyn Foo = (*y).cast_mut().unwrap(); 99 | // assert_eq!(y.foo(), 2); 100 | // assert_eq!(y.foo(), 4); 101 | 102 | // // Can cast from Foo to Baz 103 | // assert!(crate::cast_mut::(y).is_some()); 104 | // } 105 | 106 | // { 107 | // // Any to Baz fails when the type does not implement Baz 108 | // assert!(crate::cast_box::(x).is_err()); 109 | 110 | // let mut y: Box = y.cast_box().unwrap(); 111 | // // Can cast from Baz to Foo 112 | // { 113 | // let y: &mut dyn Foo = (*y).cast_mut().unwrap(); 114 | // assert_eq!(y.foo(), 8); 115 | // } 116 | 117 | // assert_eq!(y.baz(), 8); 118 | // } 119 | // } 120 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | reorder_imports = true 2 | max_width = 80 3 | -------------------------------------------------------------------------------- /traitcast/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /Cargo.lock 3 | -------------------------------------------------------------------------------- /traitcast/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "traitcast" 3 | version = "0.5.0" 4 | authors = ["Bradley Hardy "] 5 | edition = "2018" 6 | license = "MIT" 7 | description = "Cast between dynamic trait objects." 8 | repository = "https://github.com/bch29/traitcast" 9 | 10 | [dependencies] 11 | lazy_static = "1.*" 12 | inventory = "0.1.*" 13 | 14 | [dependencies.traitcast_core] 15 | version = "0.2.*" 16 | path = "../core" 17 | features = ["use_inventory"] 18 | -------------------------------------------------------------------------------- /traitcast/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2019 Bradley Hardy 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /traitcast/src/lib.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | 3 | ## Casting from `Any` 4 | 5 | In the standard library, the std::any::Any trait comes with downcast methods 6 | which let you cast from an `Any` trait object to a concrete type. 7 | 8 | ```rust 9 | let x: i32 = 7; 10 | let y: &dyn std::any::Any = &x; 11 | 12 | // Cast to i32 succeeds because x: i32 13 | assert_eq!(y.downcast_ref::(), Some(&7)); 14 | // Cast to f32 fails 15 | assert_eq!(y.downcast_ref::(), None); 16 | ``` 17 | 18 | However, it is not possible to downcast to a trait object. 19 | 20 | ```compile_fail 21 | trait Foo { 22 | fn foo(&self) -> i32; 23 | } 24 | 25 | struct A { 26 | x: i32 27 | } 28 | 29 | impl Foo for A { 30 | fn foo(&self) -> i32 { 31 | self.x 32 | } 33 | } 34 | 35 | let x = A { x: 7 }; 36 | let y: &dyn std::any::Any = &x; 37 | 38 | // This cast is not possible, because it is only possible to cast to types that 39 | // are Sized. Among other things, this precludes trait objects. 40 | let z: Option<&dyn Foo> = y.downcast_ref(); 41 | ``` 42 | 43 | ## Traitcast 44 | 45 | This library provides a way of casting between different trait objects. 46 | 47 | ```rust 48 | use traitcast::{TraitcastFrom, Traitcast}; 49 | 50 | // Extending `TraitcastFrom` is optional. This allows `Foo` objects themselves 51 | // to be cast to other trait objects. If you do not extend `TraitcastFrom`, 52 | // then Foo may only be cast into, not out of. 53 | trait Foo: TraitcastFrom { 54 | fn foo(&self) -> i32; 55 | } 56 | 57 | trait Bar: TraitcastFrom { 58 | fn bar(&mut self) -> i32; 59 | } 60 | 61 | struct A { 62 | x: i32 63 | } 64 | 65 | // No implementation of TraitcastFrom is necessary, because it is covered by 66 | // the blanket impl for any sized type with a static lifetime. 67 | impl Foo for A { 68 | fn foo(&self) -> i32 { 69 | self.x 70 | } 71 | } 72 | 73 | impl Bar for A { 74 | fn bar(&mut self) -> i32 { 75 | self.x *= 2; 76 | self.x 77 | } 78 | } 79 | 80 | // Register the traits. 81 | 82 | // For each struct that implements each trait, register the implementation. 83 | traitcast::traitcast!(struct A: Foo, Bar); 84 | 85 | fn main() { 86 | let mut x = A { x: 7 }; 87 | 88 | { 89 | let x: &dyn Foo = &x; 90 | // Test whether x is of a type that implements Bar. 91 | assert!(traitcast::implements_trait::(x)); 92 | } 93 | 94 | { 95 | let x: &dyn Bar = &x; 96 | // Cast an immutable reference using the `cast_ref` method (via the 97 | // `Traitcast` trait, which is blanket implemented for all pairs of 98 | // traits that may be cast between). 99 | let x: &dyn Foo = x.cast_ref().unwrap(); 100 | assert_eq!(x.foo(), 7); 101 | 102 | // We can also cast using the top-level `cast_ref` function, which can 103 | // be more convenient when type arguments cannot be inferred. 104 | assert!(traitcast::cast_ref::(x).is_some()); 105 | } 106 | 107 | { 108 | let x: &mut dyn Foo = &mut x; 109 | // Cast a mutable reference using the `cast_mut` method 110 | let x: &mut dyn Bar = x.cast_mut().unwrap(); 111 | assert_eq!(x.bar(), 14); 112 | } 113 | 114 | { 115 | // We can cast from `Any` too! 116 | let y: Box = Box::new(x); 117 | // Cast a boxed reference 118 | let z: Box = y.cast_box().unwrap(); 119 | assert_eq!(z.foo(), 14); 120 | } 121 | } 122 | ``` 123 | 124 | */ 125 | 126 | #[cfg(test)] 127 | pub mod tests; 128 | 129 | use std::any::Any; 130 | 131 | pub use traitcast_core::TraitcastFrom; 132 | pub use traitcast_core::traitcast; 133 | use traitcast_core::inventory::build_registry; 134 | use traitcast_core::Registry; 135 | 136 | lazy_static::lazy_static! { 137 | /// This is a global table of all the trait objects that can be cast into. 138 | /// Each entry is a CastIntoTrait, i.e. a table of the implementations of a 139 | /// castable trait. 140 | static ref GLOBAL_REGISTRY: Registry = 141 | build_registry(); 142 | } 143 | 144 | /// A convenience trait with a blanket implementation that adds methods to cast 145 | /// from any trait that implements TraitcastFrom, to target with a static 146 | /// lifetime. 147 | pub trait Traitcast { 148 | /// A convenience method that wraps the top-level `cast_ref` function. 149 | fn cast_ref(&self) -> Option<&To>; 150 | 151 | /// A convenience method that wraps the top-level `cast_mut` function. 152 | fn cast_mut(&mut self) -> Option<&mut To>; 153 | 154 | /// A convenience method that wraps the top-level `cast_box` function. 155 | fn cast_box(self: Box) -> Result, Box>; 156 | } 157 | 158 | impl Traitcast for From 159 | where 160 | From: TraitcastFrom + ?Sized, 161 | To: ?Sized + 'static, 162 | { 163 | /// Tries to cast self to a different dynamic trait object. This will 164 | /// always return None if the implementation of the target trait, for the 165 | /// concrete type of self, has not been registered via 166 | /// `traitcast!`. 167 | fn cast_ref(&self) -> Option<&To> { 168 | cast_ref(self) 169 | } 170 | 171 | /// Tries to cast the self to a different dynamic trait object. This will 172 | /// always return None if the implementation of the target trait, for the 173 | /// concrete type of self, has not been registered via 174 | /// `traitcast!`. 175 | fn cast_mut(&mut self) -> Option<&mut To> { 176 | cast_mut(self) 177 | } 178 | 179 | /// Tries to cast self to a boxed dynamic trait object. This will always 180 | /// return Err if the implementation of the target trait, for the concrete 181 | /// type of self, has not been registered via `traitcast!`. 182 | fn cast_box(self: Box) -> Result, Box> { 183 | cast_box(self) 184 | } 185 | } 186 | 187 | /// Tests whether the given value is castable to some trait object. This will 188 | /// always return `false` if the implementation of the target trait, for the 189 | /// concrete type of x, has not been registered via `traitcast!`. 190 | pub fn implements_trait(x: &From) -> bool 191 | where 192 | From: TraitcastFrom + ?Sized, 193 | To: ?Sized + 'static, 194 | { 195 | cast_ref::(x).is_some() 196 | } 197 | 198 | /// Tries to cast the given pointer to a dynamic trait object. This will always 199 | /// return Err if the implementation of the target trait, for the concrete type 200 | /// of x, has not been registered via `traitcast!`. 201 | pub fn cast_box(x: Box) -> Result, Box> 202 | where 203 | From: TraitcastFrom + ?Sized, 204 | To: ?Sized + 'static, 205 | { 206 | GLOBAL_REGISTRY 207 | .cast_into::() 208 | .expect("Calling cast_box to cast into an unregistered trait object") 209 | .from_box(x) 210 | } 211 | 212 | /// Tries to cast the given mutable reference to a dynamic trait object. This 213 | /// will always return None if the implementation of the target trait, for the 214 | /// concrete type of x, has not been registered via `traitcast!`. 215 | pub fn cast_mut<'a, From, To>(x: &'a mut From) -> Option<&'a mut To> 216 | where 217 | From: TraitcastFrom + ?Sized, 218 | To: ?Sized + 'static, 219 | { 220 | GLOBAL_REGISTRY 221 | .cast_into::() 222 | .expect("Calling cast_mut to cast into an unregistered trait object") 223 | .from_mut(x) 224 | } 225 | 226 | /// Tries to cast the given reference to a dynamic trait object. This will 227 | /// always return None if the implementation of the target trait, for the 228 | /// concrete type of x, has not been registered via `traitcast!`. 229 | pub fn cast_ref<'a, From, To>(x: &'a From) -> Option<&'a To> 230 | where 231 | From: TraitcastFrom + ?Sized, 232 | To: ?Sized + 'static, 233 | { 234 | GLOBAL_REGISTRY 235 | .cast_into::() 236 | .expect("Calling cast_ref to cast into an unregistered trait object") 237 | .from_ref(x) 238 | } 239 | -------------------------------------------------------------------------------- /traitcast/src/tests.rs: -------------------------------------------------------------------------------- 1 | #![cfg(test)] 2 | 3 | use std::any::Any; 4 | 5 | mod traits { 6 | pub trait Foo: crate::TraitcastFrom { 7 | fn foo(&mut self) -> i64; 8 | } 9 | 10 | pub trait Bar: crate::TraitcastFrom { 11 | fn bar(&self) -> i64; 12 | } 13 | 14 | pub trait Baz: crate::TraitcastFrom { 15 | fn baz(self: Box) -> i64; 16 | } 17 | } 18 | 19 | mod structs { 20 | use crate::traitcast; 21 | 22 | use crate::tests::traits::{self, Bar, Foo}; 23 | pub struct A { 24 | pub x: i64, 25 | } 26 | 27 | pub struct B { 28 | pub y: i64, 29 | } 30 | 31 | impl Foo for A { 32 | fn foo(&mut self) -> i64 { 33 | self.x += 1; 34 | self.x 35 | } 36 | } 37 | 38 | impl Bar for A { 39 | fn bar(&self) -> i64 { 40 | self.x 41 | } 42 | } 43 | 44 | impl Foo for B { 45 | fn foo(&mut self) -> i64 { 46 | self.y *= 2; 47 | self.y 48 | } 49 | } 50 | 51 | impl traits::Baz for B { 52 | fn baz(self: Box) -> i64 { 53 | self.y 54 | } 55 | } 56 | 57 | traitcast!(struct A: Foo, Bar); 58 | traitcast!(struct B: Foo, traits::Baz); 59 | } 60 | 61 | use structs::*; 62 | use traits::*; 63 | 64 | use crate::Traitcast; 65 | 66 | #[test] 67 | fn test_traitcast() { 68 | let mut x: Box = Box::new(A { x: 0 }); 69 | let mut y: Box = Box::new(B { y: 1 }); 70 | 71 | { 72 | // Can cast from Any to Bar 73 | let x: &dyn Bar = (*x).cast_ref().unwrap(); 74 | assert_eq!(x.bar(), 0); 75 | 76 | // Can cast from Bar to Foo 77 | assert!(crate::cast_ref::(x).is_some()); 78 | 79 | // Any to Bar cast fails when the type does not implement Bar 80 | assert!(crate::cast_ref::(&*y).is_none()); 81 | } 82 | 83 | { 84 | // Can cast from Any to Foo 85 | let x: &mut dyn Foo = (*x).cast_mut().unwrap(); 86 | assert_eq!(x.foo(), 1); 87 | assert_eq!(x.foo(), 2); 88 | 89 | // Can cast from Foo to Bar 90 | let x: &mut dyn Bar = x.cast_mut().unwrap(); 91 | assert_eq!(x.bar(), 2); 92 | 93 | // Can also cast B from Any to Foo 94 | let y: &mut dyn Foo = (*y).cast_mut().unwrap(); 95 | assert_eq!(y.foo(), 2); 96 | assert_eq!(y.foo(), 4); 97 | 98 | // Can cast from Foo to Baz 99 | assert!(crate::cast_mut::(y).is_some()); 100 | } 101 | 102 | { 103 | // Any to Baz fails when the type does not implement Baz 104 | assert!(crate::cast_box::(x).is_err()); 105 | 106 | let mut y: Box = y.cast_box().unwrap(); 107 | // Can cast from Baz to Foo 108 | { 109 | let y: &mut dyn Foo = (*y).cast_mut().unwrap(); 110 | assert_eq!(y.foo(), 8); 111 | } 112 | 113 | { 114 | // Can cast back up to the original type 115 | let y: &mut B = (*y).cast_mut().unwrap(); 116 | assert_eq!(y.y, 8); 117 | } 118 | 119 | assert_eq!(y.baz(), 8); 120 | } 121 | } 122 | --------------------------------------------------------------------------------