pub trait DynClone {
#[doc(hidden)]
fn __clone_box(&self) -> *mut ();
}
impl<T: Clone + 'static> DynClone for T {
fn __clone_box(&self) -> *mut () {
Box::into_raw(Box::<T>::new(self.clone())) as *mut ()
}
}
#[macro_export]
macro_rules! clone_trait_object {
($trait:path) => {
impl ::std::clone::Clone for ::std::boxed::Box<dyn $trait> {
fn clone(&self) -> Self {
let mut fat_ptr: *const (dyn $trait) = &**self;
unsafe {
let data_ptr = &mut fat_ptr as *mut *const (dyn $trait) as *mut *mut ();
*data_ptr = $crate::DynClone::__clone_box(&**self);
::std::boxed::Box::from_raw(fat_ptr as *mut (dyn $trait))
}
}
}
};
}
#[cfg(test)]
mod tests {
use super::*;
trait Core: DynClone + Send + Sync {
fn tag(&self) -> &'static str;
fn count(&self) -> i64;
}
crate::clone_trait_object!(Core);
#[derive(Clone)]
struct Real { tag: &'static str, n: i64 }
impl Core for Real {
fn tag(&self) -> &'static str { self.tag }
fn count(&self) -> i64 { self.n }
}
#[test]
fn box_dyn_core_is_clonable() {
let a: Box<dyn Core> = Box::new(Real { tag: "first", n: 42 });
let b: Box<dyn Core> = a.clone();
assert_eq!(a.tag(), b.tag());
assert_eq!(a.count(), b.count());
}
#[test]
fn clones_are_distinct_allocations() {
let a: Box<dyn Core> = Box::new(Real { tag: "x", n: 7 });
let b: Box<dyn Core> = a.clone();
let a_ptr = Box::as_ref(&a) as *const dyn Core as *const ();
let b_ptr = Box::as_ref(&b) as *const dyn Core as *const ();
assert_ne!(a_ptr, b_ptr, "clone should allocate a fresh Box");
assert_eq!(a.count(), b.count());
}
#[test]
fn with_returns_clonable_boxed_trait() {
trait Bumpable: DynClone {
fn n(&self) -> i64;
fn bump(&self) -> Box<dyn Bumpable>;
}
crate::clone_trait_object!(Bumpable);
#[derive(Clone)]
struct Impl(i64);
impl Bumpable for Impl {
fn n(&self) -> i64 { self.0 }
fn bump(&self) -> Box<dyn Bumpable> { Box::new(Impl(self.0 + 1)) }
}
let a: Box<dyn Bumpable> = Box::new(Impl(1));
let b = a.bump(); let c = b.clone(); assert_eq!(a.n(), 1);
assert_eq!(b.n(), 2);
assert_eq!(c.n(), 2);
}
}