Skip to main content

clone_behavior/
deep.rs

1#![expect(clippy::absolute_paths, reason = "there's a lot of random types used")]
2#![warn(clippy::missing_inline_in_public_items, reason = "almost everything is very short")]
3
4use crate::call_varargs_macro;
5use crate::speed::{Fast, MaybeSlow, Speed};
6
7
8/// Get deep clones of a value, which do not share any semantically-important mutable state.
9///
10/// The goal is that the clones and their source appear to act completely independently,
11/// at least from their public interfaces; mutating or dropping one clone (among possible actions)
12/// should have no potentially-observable effect on any other independent clone.
13///
14/// Note also that a deep clone and its source may reference the same immutable data, even
15/// if the public interface of the type could be used to confirm that two deep clones are
16/// (or were) associated. (However, there should not be a way for either the source or any
17/// independent clone to affect that data's value or address in memory.)
18///
19/// Side-channel attacks should be ignored in implementing this trait. The point is to provide deep
20/// clones, not cryptographic-quality guarantees about how those deep clones are performed.
21///
22/// # Exceptions
23/// - A type implementing `DeepClone` may specify that certain methods or accesses are not
24///   subject to these guarantees.
25/// - `Debug` should be assumed to be an exception. Intentionally worsening the life of someone
26///   debugging your type is not a goal.
27/// - Shared mutable data which is written to but never read, or is read with effects that can only
28///   be publicly observed through exceptions, is acceptable.
29///
30/// # Other edge cases
31/// - A user calling `as_ptr()` and friends on references, to do address comparisons: if the
32///   address returned could change, then even if the underlying value at a new address would be
33///   the same, "the address of the data" should be considered mutable state. If that mutable state
34///   can be affected by the actions of the source of the independent clone or by other clones,
35///   then the address of the data must not be publicly exposed by the type's interface.
36/// - Copy-on-write data: while likely permissible if properly encapsulated, presumably the source,
37///   the independent clone, or both are going to be mutated; it'd be best to give the independent
38///   clone a unique owned copy of the data. Further, if some sort of reference-counted
39///   copy-on-write scheme is used where the last clone to refer to the data doesn't need to copy
40///   it, then the reference count used to determine that is shared mutable state. This can be
41///   avoided by simply giving the new independent clone an unique owned copy of the data, as
42///   should likely be done anyway.
43pub trait DeepClone<S: Speed>: Sized {
44    /// Get a deep clone of a value, which does not share any semantically-important mutable state.
45    ///
46    /// The goal is that the clone and its source appear to act completely independently, at least
47    /// from their public interfaces; mutating or dropping one clone (among possible actions)
48    /// should have no potentially-observable effect on any other deep clone.
49    ///
50    /// Read [`DeepClone`] for more.
51    #[must_use]
52    fn deep_clone(&self) -> Self;
53}
54
55/// Quickly get deep clones of a value, which do not share any semantically-important mutable state.
56///
57/// See [`DeepClone`] for more.
58pub trait FastDeepClone: DeepClone<Fast> {
59    /// Get a deep clone of a value, which does not share any semantically-important mutable state.
60    ///
61    /// The goal is that the clone and its source appear to act completely independently, at least
62    /// from their public interfaces; mutating or dropping one clone (among possible actions)
63    /// should have no potentially-observable effect on any other deep clone.
64    ///
65    /// See [`DeepClone`] for more.
66    #[must_use]
67    fn fast_deep_clone(&self) -> Self;
68}
69
70impl<T: DeepClone<Fast>> FastDeepClone for T {
71    #[inline]
72    fn fast_deep_clone(&self) -> Self {
73        self.deep_clone()
74    }
75}
76
77
78macro_rules! impl_copy {
79    ($($types:ty),*) => {
80        $(
81            impl<S: Speed> DeepClone<S> for $types {
82                #[inline]
83                fn deep_clone(&self) -> Self {
84                    *self
85                }
86            }
87        )*
88    };
89}
90
91macro_rules! int_impls {
92    ($($num:ident),* $(,)?) => {
93        $(
94            impl_copy!($num, ::core::num::NonZero<$num>);
95        )*
96    };
97}
98
99int_impls!(
100    i8, i16, i32, i64, i128, isize,
101    u8, u16, u32, u64, u128, usize,
102);
103
104macro_rules! non_recursive_fast {
105    ($($({for $($bounds:tt)+})? $type:ty),* $(,)?) => {
106        $(
107            impl<S: Speed, $($($bounds)+)?> DeepClone<S> for $type {
108                #[inline]
109                fn deep_clone(&self) -> Self {
110                    self.clone()
111                }
112            }
113        )*
114    };
115}
116
117non_recursive_fast! {
118    f32, f64, bool, char, (),
119    core::alloc::Layout,
120    core::any::TypeId,
121    core::cmp::Ordering,
122    {for T} core::iter::Empty<T>,
123    {for T: ?Sized} core::marker::PhantomData<T>,
124    core::marker::PhantomPinned,
125    {for T} core::mem::Discriminant<T>,
126    core::ops::RangeFull,
127    core::sync::atomic::Ordering,
128    core::time::Duration,
129}
130
131#[cfg(feature = "std")]
132non_recursive_fast! {
133    std::time::Instant,
134    std::thread::ThreadId,
135}
136
137macro_rules! atomic {
138    ($($name:ident $bits:literal),* $(,)?) => {
139        $(
140            #[cfg(target_has_atomic = $bits)]
141            impl<S: Speed> DeepClone<S> for core::sync::atomic::$name {
142                #[inline]
143                fn deep_clone(&self) -> Self {
144                    Self::new(self.load(core::sync::atomic::Ordering::Relaxed))
145                }
146            }
147        )*
148    };
149}
150
151atomic! {
152    AtomicBool    "8",
153    AtomicI8      "8", AtomicU8      "8",
154    AtomicI16    "16", AtomicU16    "16",
155    AtomicI32    "32", AtomicU32    "32",
156    AtomicI64    "64", AtomicU64    "64",
157    AtomicIsize "ptr", AtomicUsize "ptr",
158}
159
160macro_rules! function {
161    ($($args:ident),*) => {
162        impl<S: Speed, R, $($args),*> DeepClone<S> for fn($($args),*) -> R {
163            #[inline]
164            fn deep_clone(&self) -> Self {
165                *self
166            }
167        }
168    };
169}
170
171function!();
172call_varargs_macro!(function);
173
174macro_rules! make_tuple_macro {
175    ($name:ident, $speed:ident, $dollar:tt) => {
176        macro_rules! $name {
177            ($dollar($dollar args:ident),+) => {
178                impl<$dollar($dollar args: DeepClone<$speed>),+> DeepClone<$speed>
179                for ($dollar($dollar args,)+)
180                {
181                    #[inline]
182                    fn deep_clone(&self) -> Self {
183                        #[expect(
184                            non_snake_case,
185                            reason = "using `Tn` as the variable of type `Tn`",
186                        )]
187                        let ($dollar($dollar args,)+) = self;
188                        (
189                            $dollar($dollar args.deep_clone(),)+
190                        )
191                    }
192                }
193            };
194        }
195    };
196}
197
198make_tuple_macro!(tuple_fast, Fast, $);
199make_tuple_macro!(tuple_slow, MaybeSlow, $);
200
201call_varargs_macro!(tuple_fast);
202call_varargs_macro!(tuple_slow);
203
204macro_rules! recursive {
205    (
206        $(
207            $(#[$meta:meta])*
208            $({for ($($special_bounds:ident)*) {$($where_bounds:tt)*} $($bounds:tt)*})?
209            $type:ty
210            {|$self:ident| $($body:tt)*}
211        ),*
212        $(,)?
213    ) => {
214        $(
215            impl<$($($special_bounds: DeepClone<Fast>,)* $($bounds)*)?> DeepClone<Fast>
216            for $type
217            where
218                $($($where_bounds)*)?
219            {
220                $(#[$meta])*
221                #[inline]
222                fn deep_clone(&$self) -> Self {
223                    $($body)*
224                }
225            }
226
227
228            impl<$($($special_bounds: DeepClone<MaybeSlow>,)* $($bounds)*)?> DeepClone<MaybeSlow>
229            for $type
230            where
231                $($($where_bounds)*)?
232            {
233                $(#[$meta])*
234                #[inline]
235                fn deep_clone(&$self) -> Self {
236                    $($body)*
237                }
238            }
239        )*
240    };
241}
242
243recursive! {
244    {for (T) {T: ?Sized} const N: usize} [T; N] {|self| {
245        self.each_ref().map(T::deep_clone)
246    }},
247    {for (T) {}} Option<T> {|self| {
248        self.as_ref().map(T::deep_clone)
249    }},
250    {for (T E) {}} Result<T, E> {|self| {
251        self.as_ref()
252            .map(T::deep_clone)
253            .map_err(E::deep_clone)
254    }},
255    {for (T) {}} core::mem::ManuallyDrop<T> {|self| {
256        Self::new(T::deep_clone(self))
257    }},
258    {for (T) {T: Copy}} core::cell::Cell<T> {|self| {
259        Self::new(T::deep_clone(&self.get()))
260    }},
261    /// # Panics
262    /// Panics if the value is currently mutably borrowed.
263    {for (T) {}} core::cell::RefCell<T> {|self| {
264        Self::new(T::deep_clone(&self.borrow()))
265    }},
266}
267
268#[cfg(feature = "alloc")]
269recursive! {
270    {for (T) {T: ?Sized}} alloc::rc::Rc<T> {|self| {
271        Self::new(T::deep_clone(self))
272    }},
273    {for (T) {T:}} alloc::boxed::Box<T> {|self| {
274        Self::new(T::deep_clone(self))
275    }},
276    {for (T) {T: ?Sized}} core::pin::Pin<alloc::rc::Rc<T>> {|self| {
277        alloc::rc::Rc::pin(T::deep_clone(self))
278    }},
279    {for (T) {T: ?Sized}} alloc::rc::Weak<T> {|self| {
280        if let Some(rc) = self.upgrade() {
281            alloc::rc::Rc::downgrade(&alloc::rc::Rc::new(T::deep_clone(&rc)))
282        } else {
283            Self::new()
284        }
285    }},
286    {for (T) {T: ?Sized}} alloc::sync::Arc<T> {|self| {
287        Self::new(T::deep_clone(self))
288    }},
289    {for (T) {T: ?Sized}} core::pin::Pin<alloc::sync::Arc<T>> {|self| {
290        alloc::sync::Arc::pin(T::deep_clone(self))
291    }},
292    {for (T) {T: ?Sized}} alloc::sync::Weak<T> {|self| {
293        if let Some(arc) = self.upgrade() {
294            alloc::sync::Arc::downgrade(&alloc::sync::Arc::new(T::deep_clone(&arc)))
295        } else {
296            Self::new()
297        }
298    }},
299}
300
301#[cfg(feature = "std")]
302recursive! {
303    /// # Panics
304    /// Panics if the `RwLock` is poisoned.
305    {for (T) {}} std::sync::RwLock<T> {|self| {
306        let lock_result: Result<_, std::sync::PoisonError<_>> = self.read();
307        #[expect(clippy::unwrap_used, reason = "Unwrapping poison")]
308        Self::new(T::deep_clone(&lock_result.unwrap()))
309    }},
310    /// # Panics or Deadlocks
311    /// Panics if the `Mutex` is poisoned.
312    ///
313    /// Will either panic or deadlock if the current thread already holds the mutex.
314    {for (T) {}} std::sync::Mutex<T> {|self| {
315        let lock_result: Result<_, std::sync::PoisonError<_>> = self.lock();
316        #[expect(clippy::unwrap_used, reason = "Unwrapping poison")]
317        Self::new(T::deep_clone(&lock_result.unwrap()))
318    }},
319}
320
321// constant or slower: ranges, ops::Bound,
322
323macro_rules! map_and_collect {
324    ($($t:ident $({$($where_bounds:tt)*})? $type:ty),* $(,)?) => {
325        $(
326            #[cfg(feature = "alloc")]
327            impl<$t: DeepClone<MaybeSlow>> DeepClone<MaybeSlow>
328            for $type
329            where
330                $($($where_bounds)*)?
331            {
332                #[inline]
333                fn deep_clone(&self) -> Self {
334                    self.iter()
335                        .map($t::deep_clone)
336                        .collect()
337                }
338            }
339        )*
340    };
341}
342
343map_and_collect! {
344    T alloc::boxed::Box<[T]>,
345    T alloc::vec::Vec<T>,
346    T alloc::collections::VecDeque<T>,
347    T alloc::collections::LinkedList<T>,
348    T {T: Ord} alloc::collections::BTreeSet<T>,
349    T {T: Ord} alloc::collections::BinaryHeap<T>,
350}
351
352#[cfg(feature = "alloc")]
353impl<T: DeepClone<MaybeSlow>> DeepClone<MaybeSlow>
354for core::pin::Pin<alloc::boxed::Box<[T]>>
355{
356    #[inline]
357    fn deep_clone(&self) -> Self {
358        let new_box = self.iter()
359            .map(T::deep_clone)
360            .collect::<alloc::boxed::Box<[T]>>();
361
362        alloc::boxed::Box::into_pin(new_box)
363    }
364}
365
366#[cfg(feature = "alloc")]
367impl DeepClone<MaybeSlow> for alloc::boxed::Box<str> {
368    #[inline]
369    fn deep_clone(&self) -> Self {
370        self.clone()
371    }
372}
373
374#[cfg(feature = "alloc")]
375impl DeepClone<MaybeSlow> for core::pin::Pin<alloc::boxed::Box<str>> {
376    #[inline]
377    fn deep_clone(&self) -> Self {
378        self.clone()
379    }
380}
381
382#[cfg(feature = "alloc")]
383impl<K, V> DeepClone<MaybeSlow> for alloc::collections::BTreeMap<K, V>
384where
385    K: DeepClone<MaybeSlow> + Ord,
386    V: DeepClone<MaybeSlow>,
387{
388    #[inline]
389    fn deep_clone(&self) -> Self {
390        self.iter()
391            .map(|(key, val)| {
392                (
393                    K::deep_clone(key),
394                    V::deep_clone(val),
395                )
396            })
397            .collect()
398    }
399}
400
401#[cfg(feature = "std")]
402impl<T, S> DeepClone<MaybeSlow> for std::collections::HashSet<T, S>
403where
404    T: DeepClone<MaybeSlow> + Eq + core::hash::Hash,
405    S: core::hash::BuildHasher + Default,
406{
407    #[inline]
408    fn deep_clone(&self) -> Self {
409        self.iter()
410            .map(T::deep_clone)
411            .collect()
412    }
413}
414
415#[cfg(feature = "std")]
416impl<K, V, S> DeepClone<MaybeSlow> for std::collections::HashMap<K, V, S>
417where
418    K: DeepClone<MaybeSlow> + Eq + core::hash::Hash,
419    V: DeepClone<MaybeSlow>,
420    S: core::hash::BuildHasher + Default,
421{
422    #[inline]
423    fn deep_clone(&self) -> Self {
424        self.iter()
425            .map(|(key, val)| {
426                (
427                    K::deep_clone(key),
428                    V::deep_clone(val),
429                )
430            })
431            .collect()
432    }
433}
434
435impl<S: Speed> DeepClone<S> for core::convert::Infallible {
436    #[expect(
437        clippy::missing_inline_in_public_items,
438        clippy::uninhabited_references,
439        reason = "this is unreachable",
440    )]
441    fn deep_clone(&self) -> Self {
442        *self
443    }
444}
445
446// TODO: iterators, other pinned things