clone_behavior/mirrored.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, FastSpeed, MaybeSlow, Speed};
6
7
8/// Get clones that share all semantically-important mutable state.
9///
10/// The goal is that mutating one mirrored clone affects every clone. Different mirrored clones
11/// should, from their public interfaces, act identically, with some exceptions like memory
12/// addresses.
13///
14/// This can be achieved via reference counting (e.g., [`Rc`] or [`Arc`]), or by simply
15/// not having any mutable state to share at all (e.g. a zero-sized type like `()`, or a reference
16/// to a type without internal mutability, like `&'static str`).
17///
18/// Per-clone mutable data is permissible, so long as the effects of mutating that data do not
19/// cause mirrored clones to behave differently in a potentially-observable way.
20/// Clones of types like `Option<T>` or `u32` are not considered to be mirrored clones, as
21/// counterexamples.
22///
23/// # Exceptions
24/// - A type implementing `MirroredClone` may specify that certain methods or accesses are not
25/// subject to these guarantees.
26/// - `Debug` should be assumed to be an exception. Intentionally worsening the life of someone
27/// debugging your type is not a goal.
28/// - Memory addresses which are different per-clone, but are not mutated by that clone (except
29/// when a user moves the value). Trivially, mirrored clones do not have the same address,
30/// and some of their data may be inlined into the type. If any reference to that data is exposed,
31/// a user would likely be able to determine what data is inlined into the type and what is
32/// inlined. That's already visible in the source code, anyway. Unless some semantically-important
33/// function of the type depends on the address of it or its data, it's unimportant.
34/// - TLDR of above bullet: users should not assume that references returned from different mirrored
35/// clones refer to the same value, only that those values *behave* the same.
36///
37/// [`Rc`]: std::rc::Rc
38/// [`Arc`]: std::sync::Arc
39pub trait MirroredClone<S: Speed>: Sized {
40 /// Get a clone that shares all semantically-important mutable state with its source.
41 ///
42 /// The goal is that mutating one mirrored clone affects every clone. Different mirrored clones
43 /// should, from their public interfaces, act identically, with some exceptions like memory
44 /// addresses.
45 ///
46 /// Read [`MirroredClone`] for more.
47 #[inline]
48 #[must_use]
49 fn fast_mirrored_clone(&self) -> Self where S: FastSpeed {
50 self.mirrored_clone()
51 }
52
53 /// Get a clone that shares all semantically-important mutable state with its source.
54 ///
55 /// The goal is that mutating one mirrored clone affects every clone. Different mirrored clones
56 /// should, from their public interfaces, act identically, with some exceptions like memory
57 /// addresses.
58 ///
59 /// Read [`MirroredClone`] for more.
60 #[must_use]
61 fn mirrored_clone(&self) -> Self;
62}
63
64
65macro_rules! non_recursive_fast {
66 ($($({for $($bounds:tt)+})? $type:ty),* $(,)?) => {
67 $(
68 impl<S: Speed, $($($bounds)+)?> MirroredClone<S> for $type {
69 #[inline]
70 fn mirrored_clone(&self) -> Self {
71 self.clone()
72 }
73 }
74 )*
75 };
76}
77
78non_recursive_fast! {
79 (),
80 {for T} core::iter::Empty<T>,
81 {for T: ?Sized} core::marker::PhantomData<T>,
82 core::marker::PhantomPinned,
83 core::ops::RangeFull,
84}
85
86#[cfg(feature = "alloc")]
87macro_rules! refcounted {
88 ($($t:ident $refcounted:ty),* $(,)?) => {
89 $(
90 impl<S: Speed, $t: ?Sized> MirroredClone<S> for $refcounted {
91 #[inline]
92 fn mirrored_clone(&self) -> Self {
93 self.clone()
94 }
95 }
96 )*
97 };
98}
99
100#[cfg(feature = "alloc")]
101refcounted!(
102 T alloc::rc::Rc<T>,
103 T alloc::rc::Weak<T>,
104 T alloc::sync::Arc<T>,
105 T alloc::sync::Weak<T>,
106);
107
108macro_rules! function {
109 ($($args:ident),*) => {
110 impl<S: Speed, R, $($args),*> MirroredClone<S> for fn($($args),*) -> R {
111 #[inline]
112 fn mirrored_clone(&self) -> Self {
113 *self
114 }
115 }
116 };
117}
118
119macro_rules! pinned_refcounted {
120 ($($t:ident $refcounted:ty),* $(,)?) => {
121 $(
122 #[cfg(feature = "alloc")]
123 impl<S: Speed, $t: ?Sized> MirroredClone<S> for core::pin::Pin<$refcounted> {
124 #[inline]
125 fn mirrored_clone(&self) -> Self {
126 self.clone()
127 }
128 }
129 )*
130 };
131}
132
133pinned_refcounted! {
134 T alloc::rc::Rc<T>,
135 T alloc::rc::Weak<T>,
136 T alloc::sync::Arc<T>,
137 T alloc::sync::Weak<T>,
138}
139
140function!();
141call_varargs_macro!(function);
142
143macro_rules! make_tuple_macro {
144 ($name:ident, $speed:ident, $dollar:tt) => {
145 macro_rules! $name {
146 ($dollar($dollar args:ident),+) => {
147 impl<$dollar($dollar args: MirroredClone<$speed>),+> MirroredClone<$speed>
148 for ($dollar($dollar args,)+)
149 {
150 #[inline]
151 fn mirrored_clone(&self) -> Self {
152 #[expect(
153 non_snake_case,
154 reason = "using `Tn` as the variable of type `Tn`",
155 )]
156 let ($dollar($dollar args,)+) = self;
157 (
158 $dollar($dollar args.mirrored_clone(),)+
159 )
160 }
161 }
162 };
163 }
164 };
165}
166
167make_tuple_macro!(tuple_fast, Fast, $);
168make_tuple_macro!(tuple_slow, MaybeSlow, $);
169
170call_varargs_macro!(tuple_fast);
171call_varargs_macro!(tuple_slow);
172
173impl<S: Speed> MirroredClone<S> for core::convert::Infallible {
174 #[expect(
175 clippy::missing_inline_in_public_items,
176 clippy::uninhabited_references,
177 reason = "this is unreachable",
178 )]
179 fn mirrored_clone(&self) -> Self {
180 *self
181 }
182}