macro_rules! shared_ptr_methods {
($strong:ident, $weak:ident, $strong_fn:ident, $weak_fn:ident, $unsized_fn:ident) => {
impl Cloner {
#[doc = concat!("Deep clone `", stringify!($strong), "<T>` through the cloner.")]
pub fn $strong_fn<T: DeepClone + 'static>(&mut self, src: &$strong<T>) -> $strong<T> {
let key = (
TypeId::of::<T>(),
$strong::as_ptr(src).cast::<()>() as usize,
);
match self.memo.get(&key) {
Some(Entry::Done(copy)) => return $strong::clone(stored(&**copy)),
Some(Entry::InProgress(_)) => strong_cycle::<T>(),
Some(Entry::Unreserved) => unreserved_cycle::<T>(),
None => {}
}
let copy = $strong::new_cyclic(|shell: &$weak<T>| {
let _ = self
.memo
.insert(key, Entry::InProgress(Box::new(shell.clone())));
(**src).deep_clone_in(self)
});
let _ = self
.memo
.insert(key, Entry::Done(Box::new($strong::clone(©))));
copy
}
#[doc = concat!("Deep clone an unsized `", stringify!($strong), "<U>` through the cloner.")]
#[doc = concat!("[`", stringify!($strong_fn), "`](Self::", stringify!($strong_fn), ")")]
pub fn $unsized_fn<U: ?Sized + 'static>(
&mut self,
src: &$strong<U>,
build: impl FnOnce(&mut Self) -> $strong<U>,
) -> $strong<U> {
let key = (
TypeId::of::<U>(),
$strong::as_ptr(src).cast::<()>() as usize,
);
match self.memo.get(&key) {
Some(Entry::Done(copy)) => return $strong::clone(stored(&**copy)),
Some(Entry::InProgress(_) | Entry::Unreserved) => unreserved_cycle::<U>(),
None => {}
}
let _ = self.memo.insert(key, Entry::Unreserved);
let copy = build(self);
let _ = self
.memo
.insert(key, Entry::Done(Box::new($strong::clone(©))));
copy
}
#[doc = concat!("Deep clone `", stringify!($weak), "<T>` through the cloner.")]
#[doc = concat!("[`", stringify!($strong_fn), "`](Self::", stringify!($strong_fn), ")")]
pub fn $weak_fn<T: DeepClone + 'static>(&mut self, src: &$weak<T>) -> $weak<T> {
let Some(strong) = src.upgrade() else {
return $weak::new();
};
let key = (
TypeId::of::<T>(),
$strong::as_ptr(&strong).cast::<()>() as usize,
);
if let Some(entry) = self.memo.get(&key) {
return match entry {
Entry::Done(copy) => $strong::downgrade(stored(&**copy)),
Entry::InProgress(shell) => stored::<$weak<T>>(&**shell).clone(),
Entry::Unreserved => unreserved_cycle::<T>(),
};
}
$strong::downgrade(&self.$strong_fn(&strong))
}
}
};
}
mod dyn_clone;
mod impls;
use std::{
any::{Any, TypeId, type_name},
fmt,
rc::{Rc, Weak as RcWeak},
sync::{Arc, Weak as ArcWeak},
};
#[cfg(feature = "derive")]
pub use deepclone_derive::DeepClone;
use rustc_hash::FxHashMap;
pub use crate::dyn_clone::{DynDeepClone, deep_clone_unsized_arc, deep_clone_unsized_rc};
#[derive(Default)]
pub struct Cloner {
memo: FxHashMap<(TypeId, usize), Entry>,
}
#[diagnostic::on_unimplemented(
message = "`{Self}` cannot be deep cloned",
label = "no `DeepClone` impl",
note = "derive `DeepClone` on `{Self}` if you own it, or mark the field \
`#[deepclone(clone)]` if a shallow clone is correct for it",
note = "`Rc<dyn Trait>` lands here by design. Annotate the field with \
`#[deepclone(with = deepclone::deep_clone_unsized_rc)]`"
)]
pub trait DeepClone {
fn deep_clone(&self) -> Self
where
Self: Sized,
{
self.deep_clone_in(&mut Cloner::default())
}
fn deep_clone_in(&self, cloner: &mut Cloner) -> Self;
}
enum Entry {
InProgress(Box<dyn Any>),
Unreserved,
Done(Box<dyn Any>),
}
fn stored<T: 'static>(entry: &dyn Any) -> &T {
entry
.downcast_ref::<T>()
.expect("memo entries are stored under a key carrying their own TypeId")
}
#[cold]
fn strong_cycle<T: ?Sized>() -> ! {
panic!(
"deep clone reached a cycle of strong `Rc`/`Arc` edges through `{}`; the copy of that \
object does not exist yet, so there is nothing to point at. Such a cycle also leaks \
in the original — use `Weak` for back-edges, which this crate does support.",
type_name::<T>(),
)
}
#[cold]
fn unreserved_cycle<T: ?Sized>() -> ! {
panic!(
"deep clone reached `{}` while its own copy was still being built. `Rc::new_cyclic` \
cannot reserve an unsized allocation, so a slice cannot take part in a cycle, not even \
through `Weak`.",
type_name::<T>(),
)
}
impl fmt::Debug for Cloner {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Cloner")
.field("copies", &self.memo.len())
.finish()
}
}
shared_ptr_methods!(Rc, RcWeak, rc, rc_weak, rc_unsized);
shared_ptr_methods!(Arc, ArcWeak, arc, arc_weak, arc_unsized);