1// This file is part of ICU4X. For terms of use, please see the file
2// called LICENSE at the top level of the ICU4X source tree
3// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
4
5#[cfg(feature = "alloc")]
6use alloc::borrow::{Cow, ToOwned};
7use core::mem;
8
9/// The `Yokeable<'a>` trait is implemented on the `'static` version of any zero-copy type; for
10/// example, `Cow<'static, T>` implements `Yokeable<'a>` (for all `'a`). One can use
11/// `Yokeable::Output` on this trait to obtain the "lifetime'd" value of the `Cow<'static, T>`,
12/// e.g. `<Cow<'static, T> as Yokeable<'a>'>::Output` is `Cow<'a, T>`.
13///
14/// A [`Yokeable`] type is essentially one with a covariant lifetime parameter,
15/// matched to the parameter in the trait definition. The trait allows one to cast
16/// the covariant lifetime to and from `'static`.
17///
18/// **Most of the time, if you need to implement [`Yokeable`], you should be able to use the safe
19/// [`#[derive(Yokeable)]`](yoke_derive::Yokeable) custom derive.**
20///
21/// While Rust does not yet have GAT syntax, for the purpose of this documentation
22/// we shall refer to "`Self` with a lifetime `'a`" with the syntax `Self<'a>`.
23/// Self<'static> is a stand-in for the HKT Self<'_>: lifetime -> type.
24///
25/// With this terminology, [`Yokeable`] exposes ways to cast between `Self<'static>` and `Self<'a>` generically.
26/// This is useful for turning covariant lifetimes to _dynamic_ lifetimes, where `'static` is
27/// used as a way to "erase" the lifetime.
28///
29/// # Safety
30///
31/// This trait is safe to implement on types with a _covariant_ lifetime parameter, i.e. one where
32/// [`Self::transform()`]'s body can simply be `{ self }`. This will occur when the lifetime
33/// parameter is used within references, but not in the arguments of function pointers or in mutable
34/// positions (either in `&mut` or via interior mutability)
35///
36/// This trait must be implemented on the `'static` version of such a type, e.g. one should
37/// implement `Yokeable<'a>` (for all `'a`) on `Cow<'static, T>`.
38///
39/// There are further constraints on implementation safety on individual methods.
40///
41/// # Trait bounds
42///
43/// [Compiler bug #85636](https://github.com/rust-lang/rust/issues/85636) makes it tricky to add
44/// trait bounds on `Yokeable::Output`. For more information and for workarounds, see
45/// [`crate::trait_hack`].
46///
47/// # Implementation example
48///
49/// Implementing this trait manually is unsafe. Where possible, you should use the safe
50/// [`#[derive(Yokeable)]`](yoke_derive::Yokeable) custom derive instead. We include an example
51/// in case you have your own zero-copy abstractions you wish to make yokeable.
52///
53/// ```rust
54/// # use yoke::Yokeable;
55/// # use std::borrow::Cow;
56/// # use std::{mem, ptr};
57/// struct Bar<'a> {
58/// numbers: Cow<'a, [u8]>,
59/// string: Cow<'a, str>,
60/// owned: Vec<u8>,
61/// }
62///
63/// unsafe impl<'a> Yokeable<'a> for Bar<'static> {
64/// type Output = Bar<'a>;
65/// fn transform(&'a self) -> &'a Bar<'a> {
66/// // covariant lifetime cast, can be done safely
67/// self
68/// }
69///
70/// fn transform_owned(self) -> Bar<'a> {
71/// // covariant lifetime cast, can be done safely
72/// self
73/// }
74///
75/// unsafe fn make(from: Bar<'a>) -> Self {
76/// // We're just doing mem::transmute() here, however Rust is
77/// // not smart enough to realize that Bar<'a> and Bar<'static> are of
78/// // the same size, so instead we use transmute_copy
79///
80/// // This assert will be optimized out, but is included for additional
81/// // peace of mind as we are using transmute_copy
82/// debug_assert!(mem::size_of::<Bar<'a>>() == mem::size_of::<Self>());
83/// let ptr: *const Self = (&from as *const Self::Output).cast();
84/// mem::forget(from);
85/// ptr::read(ptr)
86/// }
87///
88/// fn transform_mut<F>(&'a mut self, f: F)
89/// where
90/// F: 'static + FnOnce(&'a mut Self::Output),
91/// {
92/// unsafe { f(mem::transmute::<&mut Self, &mut Self::Output>(self)) }
93/// }
94/// }
95/// ```
96pub unsafe trait Yokeable<'a>: 'static {
97 /// This type MUST be `Self` with the `'static` replaced with `'a`, i.e. `Self<'a>`
98 type Output: 'a;
99
100 /// This method must cast `self` between `&'a Self<'static>` and `&'a Self<'a>`.
101 ///
102 /// # Implementation safety
103 ///
104 /// If the invariants of [`Yokeable`] are being satisfied, the body of this method
105 /// should simply be `{ self }`, though it's acceptable to include additional assertions
106 /// if desired.
107 fn transform(&'a self) -> &'a Self::Output;
108
109 /// This method must cast `self` between `Self<'static>` and `Self<'a>`.
110 ///
111 /// # Implementation safety
112 ///
113 /// If the invariants of [`Yokeable`] are being satisfied, the body of this method
114 /// should simply be `{ self }`, though it's acceptable to include additional assertions
115 /// if desired.
116 fn transform_owned(self) -> Self::Output;
117
118 /// This method can be used to cast away `Self<'a>`'s lifetime.
119 ///
120 /// # Safety
121 ///
122 /// The returned value must be destroyed before the data `from` was borrowing from is.
123 ///
124 /// # Implementation safety
125 ///
126 /// A safe implementation of this method must be equivalent to a transmute between
127 /// `Self<'a>` and `Self<'static>`
128 unsafe fn make(from: Self::Output) -> Self;
129
130 /// This method must cast `self` between `&'a mut Self<'static>` and `&'a mut Self<'a>`,
131 /// and pass it to `f`.
132 ///
133 /// # Implementation safety
134 ///
135 /// A safe implementation of this method must be equivalent to a pointer cast/transmute between
136 /// `&mut Self<'a>` and `&mut Self<'static>` being passed to `f`
137 ///
138 /// # Why is this safe?
139 ///
140 /// Typically covariant lifetimes become invariant when hidden behind an `&mut`,
141 /// which is why the implementation of this method cannot just be `f(self)`.
142 /// The reason behind this is that while _reading_ a covariant lifetime that has been cast to a shorter
143 /// one is always safe (this is roughly the definition of a covariant lifetime), writing
144 /// may not necessarily be safe since you could write a smaller reference to it. For example,
145 /// the following code is unsound because it manages to stuff a `'a` lifetime into a `Cow<'static>`
146 ///
147 /// ```rust,compile_fail
148 /// # use std::borrow::Cow;
149 /// # use yoke::Yokeable;
150 /// struct Foo {
151 /// str: String,
152 /// cow: Cow<'static, str>,
153 /// }
154 ///
155 /// fn unsound<'a>(foo: &'a mut Foo) {
156 /// let a: &str = &foo.str;
157 /// foo.cow.transform_mut(|cow| *cow = Cow::Borrowed(a));
158 /// }
159 /// ```
160 ///
161 /// However, this code will not compile because [`Yokeable::transform_mut()`] requires `F: 'static`.
162 /// This enforces that while `F` may mutate `Self<'a>`, it can only mutate it in a way that does
163 /// not insert additional references. For example, `F` may call `to_owned()` on a `Cow` and mutate it,
164 /// but it cannot insert a new _borrowed_ reference because it has nowhere to borrow _from_ --
165 /// `f` does not contain any borrowed references, and while we give it `Self<'a>` (which contains borrowed
166 /// data), that borrowed data is known to be valid
167 ///
168 /// Note that the `for<'b>` is also necessary, otherwise the following code would compile:
169 ///
170 /// ```rust,compile_fail
171 /// # use std::borrow::Cow;
172 /// # use yoke::Yokeable;
173 /// # use std::mem;
174 /// #
175 /// // also safely implements Yokeable<'a>
176 /// struct Bar<'a> {
177 /// num: u8,
178 /// cow: Cow<'a, u8>,
179 /// }
180 ///
181 /// fn unsound<'a>(bar: &'a mut Bar<'static>) {
182 /// bar.transform_mut(move |bar| bar.cow = Cow::Borrowed(&bar.num));
183 /// }
184 /// #
185 /// # unsafe impl<'a> Yokeable<'a> for Bar<'static> {
186 /// # type Output = Bar<'a>;
187 /// # fn transform(&'a self) -> &'a Bar<'a> {
188 /// # self
189 /// # }
190 /// #
191 /// # fn transform_owned(self) -> Bar<'a> {
192 /// # // covariant lifetime cast, can be done safely
193 /// # self
194 /// # }
195 /// #
196 /// # unsafe fn make(from: Bar<'a>) -> Self {
197 /// # let ret = mem::transmute_copy(&from);
198 /// # mem::forget(from);
199 /// # ret
200 /// # }
201 /// #
202 /// # fn transform_mut<F>(&'a mut self, f: F)
203 /// # where
204 /// # F: 'static + FnOnce(&'a mut Self::Output),
205 /// # {
206 /// # unsafe { f(mem::transmute(self)) }
207 /// # }
208 /// # }
209 /// ```
210 ///
211 /// which is unsound because `bar` could be moved later, and we do not want to be able to
212 /// self-insert references to it.
213 ///
214 /// The `for<'b>` enforces this by stopping the author of the closure from matching up the input
215 /// `&'b Self::Output` lifetime with `'a` and borrowing directly from it.
216 ///
217 /// Thus the only types of mutations allowed are ones that move around already-borrowed data, or
218 /// introduce new owned data:
219 ///
220 /// ```rust
221 /// # use std::borrow::Cow;
222 /// # use yoke::Yokeable;
223 /// struct Foo {
224 /// str: String,
225 /// cow: Cow<'static, str>,
226 /// }
227 ///
228 /// fn sound<'a>(foo: &'a mut Foo) {
229 /// foo.cow.transform_mut(move |cow| cow.to_mut().push('a'));
230 /// }
231 /// ```
232 fn transform_mut<F>(&'a mut self, f: F)
233 where
234 // be VERY CAREFUL changing this signature, it is very nuanced (see above)
235 F: 'static + for<'b> FnOnce(&'b mut Self::Output);
236}
237
238#[cfg(feature = "alloc")]
239unsafe impl<'a, T: 'static + ToOwned + ?Sized> Yokeable<'a> for Cow<'static, T>
240where
241 <T as ToOwned>::Owned: Sized,
242{
243 type Output = Cow<'a, T>;
244 #[inline]
245 fn transform(&'a self) -> &'a Cow<'a, T> {
246 // Doesn't need unsafe: `'a` is covariant so this lifetime cast is always safe
247 self
248 }
249 #[inline]
250 fn transform_owned(self) -> Cow<'a, T> {
251 // Doesn't need unsafe: `'a` is covariant so this lifetime cast is always safe
252 self
253 }
254 #[inline]
255 unsafe fn make(from: Cow<'a, T>) -> Self {
256 // i hate this
257 // unfortunately Rust doesn't think `mem::transmute` is possible since it's not sure the sizes
258 // are the same
259 debug_assert!(mem::size_of::<Cow<'a, T>>() == mem::size_of::<Self>());
260 let ptr: *const Self = (&from as *const Self::Output).cast();
261 mem::forget(from);
262 core::ptr::read(ptr)
263 }
264 #[inline]
265 fn transform_mut<F>(&'a mut self, f: F)
266 where
267 F: 'static + for<'b> FnOnce(&'b mut Self::Output),
268 {
269 // Cast away the lifetime of Self
270 unsafe { f(mem::transmute::<&'a mut Self, &'a mut Self::Output>(self)) }
271 }
272}
273
274unsafe impl<'a, T: 'static + ?Sized> Yokeable<'a> for &'static T {
275 type Output = &'a T;
276 #[inline]
277 fn transform(&'a self) -> &'a &'a T {
278 // Doesn't need unsafe: `'a` is covariant so this lifetime cast is always safe
279 self
280 }
281 #[inline]
282 fn transform_owned(self) -> &'a T {
283 // Doesn't need unsafe: `'a` is covariant so this lifetime cast is always safe
284 self
285 }
286 #[inline]
287 unsafe fn make(from: &'a T) -> Self {
288 mem::transmute(from)
289 }
290 #[inline]
291 fn transform_mut<F>(&'a mut self, f: F)
292 where
293 F: 'static + for<'b> FnOnce(&'b mut Self::Output),
294 {
295 // Cast away the lifetime of Self
296 unsafe { f(mem::transmute::<&'a mut Self, &'a mut Self::Output>(self)) }
297 }
298}
299
300#[cfg(feature = "alloc")]
301unsafe impl<'a, T: 'static> Yokeable<'a> for alloc::vec::Vec<T> {
302 type Output = alloc::vec::Vec<T>;
303 #[inline]
304 fn transform(&'a self) -> &'a alloc::vec::Vec<T> {
305 // Doesn't need unsafe: `'a` is covariant so this lifetime cast is always safe
306 self
307 }
308 #[inline]
309 fn transform_owned(self) -> alloc::vec::Vec<T> {
310 // Doesn't need unsafe: `'a` is covariant so this lifetime cast is always safe
311 self
312 }
313 #[inline]
314 unsafe fn make(from: alloc::vec::Vec<T>) -> Self {
315 from
316 }
317 #[inline]
318 fn transform_mut<F>(&'a mut self, f: F)
319 where
320 F: 'static + for<'b> FnOnce(&'b mut Self::Output),
321 {
322 // Doesn't need unsafe: `'a` is covariant so this lifetime cast is always safe
323 f(self)
324 }
325}
326