From 2f22893287be4cd71f3c98c19336e66a7065f1ff Mon Sep 17 00:00:00 2001 From: trivernis Date: Sun, 13 Mar 2022 17:01:49 +0100 Subject: [PATCH] Add TryClone implementation Signed-off-by: trivernis --- Cargo.toml | 5 +---- src/lib.rs | 12 +++++++----- src/macros.rs | 2 +- src/tests.rs | 20 ++++++++++++------- src/trait_impl/mod.rs | 2 ++ src/trait_impl/try_clone.rs | 38 +++++++++++++++++++++++++++++++++++++ 6 files changed, 62 insertions(+), 17 deletions(-) create mode 100644 src/trait_impl/mod.rs create mode 100644 src/trait_impl/try_clone.rs diff --git a/Cargo.toml b/Cargo.toml index b774263..6ee1679 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,4 @@ authors = ["trivernis"] description = "A type to store an object with all associated traits" repository = "https://github.com/Trivernis/multi-trait-object/tree/main" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dev-dependencies] -dyn-clone = "1.0.4" \ No newline at end of file +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 9be3f8b..16d3da0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,6 +3,8 @@ mod tests; pub(crate) mod macros; +mod trait_impl; +pub use trait_impl::*; use std::any::{Any, TypeId}; use std::collections::HashMap; @@ -16,7 +18,7 @@ pub struct FatPointer { } /// A container to store data with the associated type and trait objects -/// allowing for casting down to traits or the concrete type +/// allowing for casting down to trait_impl or the concrete type /// ```rust /// use multi_trait_object::prelude::*; /// use std::fmt::{Debug, Display}; @@ -33,14 +35,14 @@ pub struct FatPointer { /// ``` #[derive(Debug)] pub struct MultitraitObject { - data: *mut (), - original_typeid: TypeId, - traits: HashMap, + pub(crate) data: *mut (), + pub(crate) original_typeid: TypeId, + pub(crate) traits: HashMap, } impl MultitraitObject { /// Creates a new multitrait object from the given value - /// All traits except Any must be registered on this object + /// All trait_impl except Any must be registered on this object /// in order to access them. pub fn new(value: T) -> Self { let any_vtable = __fat_pointer!(T as dyn Any).vptr; diff --git a/src/macros.rs b/src/macros.rs index 6a4935c..6c146d0 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -30,7 +30,7 @@ macro_rules! impl_trait_object { } } -/// Registers multiple traits on a multitrait object +/// Registers multiple trait_impl on a multitrait object /// ```rust /// use multi_trait_object::prelude::*; /// use std::fmt::{Debug, Display}; diff --git a/src/tests.rs b/src/tests.rs index 77a7eab..a0ed773 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -1,8 +1,7 @@ -use dyn_clone::{clone_box, DynClone}; use std::fmt::Debug; use crate::prelude::*; -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] struct TestStruct { a: u32, test: String, @@ -27,15 +26,15 @@ impl ChangeStruct for TestStruct { } } -impl_trait_object!(TestStruct, dyn DynClone, dyn ChangeStruct, dyn Debug); +impl_trait_object!(TestStruct, dyn RawClone, dyn ChangeStruct, dyn Debug); #[test] fn it_creates_fat_pointers() { let debug_vtable1 = __fat_pointer!(TestStruct as dyn Debug).vptr; - let dclone_vtable1 = __fat_pointer!(TestStruct as dyn DynClone).vptr; + let dclone_vtable1 = __fat_pointer!(TestStruct as dyn RawClone).vptr; let debug_vtable2 = __fat_pointer!(TestStruct as dyn Debug).vptr; assert_eq!(debug_vtable1, debug_vtable2); - let dclone_vtable2 = __fat_pointer!(TestStruct as dyn DynClone).vptr; + let dclone_vtable2 = __fat_pointer!(TestStruct as dyn RawClone).vptr; assert_eq!(dclone_vtable1, dclone_vtable2); } @@ -49,8 +48,6 @@ fn it_downcasts_traits() { let mto = TestStruct::default().into_multitrait(); let debug = mto.downcast_trait::().unwrap(); let _ = format!("{:?}", debug); - let obj = mto.downcast_trait::().unwrap(); - let _new_obj = clone_box(&*obj); } #[test] @@ -79,4 +76,13 @@ fn it_downcasts_to_original() { let result = mto.downcast::().unwrap(); assert_eq!(result.a, 5); assert_eq!(result.test, String::from("Hello World")); +} + +#[test] +fn it_tries_cloning() { + let mto = TestStruct::default().into_multitrait(); + let mto2 = mto.try_clone().unwrap(); + let obj1 = mto.downcast::(); + let obj2 = mto2.downcast::(); + assert_eq!(obj1, obj2); } \ No newline at end of file diff --git a/src/trait_impl/mod.rs b/src/trait_impl/mod.rs new file mode 100644 index 0000000..45779ea --- /dev/null +++ b/src/trait_impl/mod.rs @@ -0,0 +1,2 @@ +mod try_clone; +pub use try_clone::*; \ No newline at end of file diff --git a/src/trait_impl/try_clone.rs b/src/trait_impl/try_clone.rs new file mode 100644 index 0000000..94609c1 --- /dev/null +++ b/src/trait_impl/try_clone.rs @@ -0,0 +1,38 @@ +use crate::{MultitraitObject}; + +pub trait TryClone: Sized { + fn try_clone(&self) -> Option; +} + +/// Returns a raw pointer to the cloned data. +/// This will leak the memory of the underlying pointer. +/// This trait is implemented for all types that implement clone +/// so you can pass this trait to the object constructor. This +/// way the given object can be cloned with [TryClone]. +pub unsafe trait RawClone { + #[doc(hidden)] + #[must_use] + unsafe fn raw_clone(&self) -> *mut (); +} + +unsafe impl RawClone for T { + unsafe fn raw_clone(&self) -> *mut () { + Box::into_raw(Box::new(self.clone())) as *mut () + } +} + +/// A trait that tries cloning an object and returns an option +/// with the variant depending on the result. +impl TryClone for MultitraitObject { + fn try_clone(&self) -> Option { + let clone = self.downcast_trait::()?; + let data_ptr = unsafe { + clone.raw_clone() + }; + Some(MultitraitObject { + data: data_ptr, + original_typeid: self.original_typeid.clone(), + traits: self.traits.clone(), + }) + } +} \ No newline at end of file