yoke/yokeable.rs
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#![allow(unused_qualifications)]
6
7#[cfg(feature = "alloc")]
8use alloc::borrow::{Cow, ToOwned};
9use core::marker::PhantomData;
10
11/// The `Yokeable<'a>` trait is implemented on the `'static` version of any zero-copy type; for
12/// example, `Cow<'static, T>` implements `Yokeable<'a>` (for all `'a`).
13///
14/// One can use
15/// `Yokeable::Output` on this trait to obtain the "lifetime'd" value of the `Cow<'static, T>`,
16/// e.g. `<Cow<'static, T> as Yokeable<'a>'>::Output` is `Cow<'a, T>`.
17///
18/// A [`Yokeable`] type is essentially one with a covariant lifetime parameter,
19/// matched to the parameter in the trait definition. The trait allows one to cast
20/// the covariant lifetime to and from `'static`.
21///
22/// **Most of the time, if you need to implement [`Yokeable`], you should be able to use the safe
23/// [`#[derive(Yokeable)]`](yoke_derive::Yokeable) custom derive.**
24///
25/// While Rust does not yet have GAT syntax, for the purpose of this documentation
26/// we shall refer to "`Self` with a lifetime `'a`" with the syntax `Self<'a>`.
27/// Self<'static> is a stand-in for the HKT Self<'_>: lifetime -> type.
28///
29/// With this terminology, [`Yokeable`] exposes ways to cast between `Self<'static>` and `Self<'a>` generically.
30/// This is useful for turning covariant lifetimes to _dynamic_ lifetimes, where `'static` is
31/// used as a way to "erase" the lifetime.
32///
33/// # Safety
34///
35/// This trait is safe to implement on types with a _covariant_ lifetime parameter, i.e. one where
36/// [`Self::transform()`]'s body can simply be `{ self }`. This will occur when the lifetime
37/// parameter is used within references, but not in the arguments of function pointers or in mutable
38/// positions (either in `&mut` or via interior mutability)
39///
40/// This trait must be implemented on the `'static` version of such a type, e.g. one should
41/// implement `Yokeable<'a>` (for all `'a`) on `Cow<'static, T>`.
42///
43/// This trait is also safe to implement on types that do not borrow memory.
44///
45/// There are further constraints on implementation safety on individual methods.
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/// unsafe { mem::transmute::<Self::Output, Self>(from) }
77/// }
78///
79/// fn transform_mut<F>(&'a mut self, f: F)
80/// where
81/// F: 'static + FnOnce(&'a mut Self::Output),
82/// {
83/// unsafe { f(mem::transmute::<&mut Self, &mut Self::Output>(self)) }
84/// }
85/// }
86/// ```
87pub unsafe trait Yokeable<'a>: 'static {
88 /// This type MUST be `Self` with the `'static` replaced with `'a`, i.e. `Self<'a>`
89 type Output: 'a;
90
91 /// This method must cast `self` between `&'a Self<'static>` and `&'a Self<'a>`.
92 ///
93 /// # Implementation safety
94 ///
95 /// If the invariants of [`Yokeable`] are being satisfied, the body of this method
96 /// should simply be `{ self }`, though it's acceptable to include additional assertions
97 /// if desired.
98 fn transform(&'a self) -> &'a Self::Output;
99
100 /// This method must cast `self` between `Self<'static>` and `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_owned(self) -> Self::Output;
108
109 /// This method can be used to cast away `Self<'a>`'s lifetime.
110 ///
111 /// # Safety
112 ///
113 /// The returned value must be destroyed before the data `from` was borrowing from is.
114 ///
115 /// # Implementation safety
116 ///
117 /// A safe implementation of this method must be equivalent to a transmute between
118 /// `Self<'a>` and `Self<'static>`
119 unsafe fn make(from: Self::Output) -> Self;
120
121 /// This method must cast `self` between `&'a mut Self<'static>` and `&'a mut Self<'a>`,
122 /// and pass it to `f`.
123 ///
124 /// # Implementation safety
125 ///
126 /// A safe implementation of this method must be equivalent to a pointer cast/transmute between
127 /// `&mut Self<'a>` and `&mut Self<'static>` being passed to `f`
128 ///
129 /// # Why is this safe?
130 ///
131 /// Typically covariant lifetimes become invariant when hidden behind an `&mut`,
132 /// which is why the implementation of this method cannot just be `f(self)`.
133 /// The reason behind this is that while _reading_ a covariant lifetime that has been cast to a shorter
134 /// one is always safe (this is roughly the definition of a covariant lifetime), writing
135 /// may not necessarily be safe since you could write a smaller reference to it. For example,
136 /// the following code is unsound because it manages to stuff a `'a` lifetime into a `Cow<'static>`
137 ///
138 /// ```rust,compile_fail,E0521
139 /// # use std::borrow::Cow;
140 /// # use yoke::Yokeable;
141 /// struct Foo {
142 /// str: String,
143 /// cow: Cow<'static, str>,
144 /// }
145 ///
146 /// fn unsound<'a>(foo: &'a mut Foo) {
147 /// let a: &str = &foo.str;
148 /// foo.cow.transform_mut(|cow| *cow = Cow::Borrowed(a));
149 /// }
150 /// ```
151 ///
152 /// However, this code will not compile because [`Yokeable::transform_mut()`] requires `F: 'static`.
153 /// This enforces that while `F` may mutate `Self<'a>`, it can only mutate it in a way that does
154 /// not insert additional references. For example, `F` may call `to_owned()` on a `Cow` and mutate it,
155 /// but it cannot insert a new _borrowed_ reference because it has nowhere to borrow _from_ --
156 /// `f` does not contain any borrowed references, and while we give it `Self<'a>` (which contains borrowed
157 /// data), that borrowed data is known to be valid
158 ///
159 /// Note that the `for<'b>` is also necessary, otherwise the following code would compile:
160 ///
161 /// ```rust,compile_fail,E0521
162 /// # use std::borrow::Cow;
163 /// # use yoke::Yokeable;
164 /// # use std::mem;
165 /// #
166 /// // also safely implements Yokeable<'a>
167 /// struct Bar<'a> {
168 /// num: u8,
169 /// cow: Cow<'a, u8>,
170 /// }
171 ///
172 /// fn unsound<'a>(bar: &'a mut Bar<'static>) {
173 /// bar.transform_mut(move |bar| bar.cow = Cow::Borrowed(&bar.num));
174 /// }
175 /// #
176 /// # unsafe impl<'a> Yokeable<'a> for Bar<'static> {
177 /// # type Output = Bar<'a>;
178 /// # fn transform(&'a self) -> &'a Bar<'a> {
179 /// # self
180 /// # }
181 /// #
182 /// # fn transform_owned(self) -> Bar<'a> {
183 /// # // covariant lifetime cast, can be done safely
184 /// # self
185 /// # }
186 /// #
187 /// # unsafe fn make(from: Bar<'a>) -> Self {
188 /// # let ret = mem::transmute_copy(&from);
189 /// # mem::forget(from);
190 /// # ret
191 /// # }
192 /// #
193 /// # fn transform_mut<F>(&'a mut self, f: F)
194 /// # where
195 /// # F: 'static + FnOnce(&'a mut Self::Output),
196 /// # {
197 /// # unsafe { f(mem::transmute(self)) }
198 /// # }
199 /// # }
200 /// ```
201 ///
202 /// which is unsound because `bar` could be moved later, and we do not want to be able to
203 /// self-insert references to it.
204 ///
205 /// The `for<'b>` enforces this by stopping the author of the closure from matching up the input
206 /// `&'b Self::Output` lifetime with `'a` and borrowing directly from it.
207 ///
208 /// Thus the only types of mutations allowed are ones that move around already-borrowed data, or
209 /// introduce new owned data:
210 ///
211 /// ```rust
212 /// # use std::borrow::Cow;
213 /// # use yoke::Yokeable;
214 /// struct Foo {
215 /// str: String,
216 /// cow: Cow<'static, str>,
217 /// }
218 ///
219 /// fn sound(foo: &mut Foo) {
220 /// foo.cow.transform_mut(move |cow| cow.to_mut().push('a'));
221 /// }
222 /// ```
223 ///
224 /// More formally, a reference to an object that `f` assigns to a reference
225 /// in Self<'a> could be obtained from:
226 /// - a local variable: the compiler rejects the assignment because 'a certainly
227 /// outlives local variables in f.
228 /// - a field in its argument: because of the for<'b> bound, the call to `f`
229 /// must be valid for a particular 'b that is strictly shorter than 'a. Thus,
230 /// the compiler rejects the assignment.
231 /// - a reference field in Self<'a>: this does not extend the set of
232 /// non-static lifetimes reachable from Self<'a>, so this is fine.
233 /// - one of f's captures: since F: 'static, the resulting reference must refer
234 /// to 'static data.
235 /// - a static or `thread_local` variable: ditto.
236 fn transform_mut<F>(&'a mut self, f: F)
237 where
238 // be VERY CAREFUL changing this signature, it is very nuanced (see above)
239 F: 'static + for<'b> FnOnce(&'b mut Self::Output);
240}
241
242#[cfg(feature = "alloc")]
243// Safety: Cow<'a, _> is covariant in 'a.
244unsafe impl<'a, T: 'static + ToOwned + ?Sized> Yokeable<'a> for Cow<'static, T>
245where
246 <T as ToOwned>::Owned: Sized,
247{
248 type Output = Cow<'a, T>;
249 #[inline]
250 fn transform(&'a self) -> &'a Cow<'a, T> {
251 // Doesn't need unsafe: `'a` is covariant so this lifetime cast is always safe
252 self
253 }
254 #[inline]
255 fn transform_owned(self) -> Cow<'a, T> {
256 // Doesn't need unsafe: `'a` is covariant so this lifetime cast is always safe
257 self
258 }
259 #[inline]
260 unsafe fn make(from: Cow<'a, T>) -> Self {
261 core::mem::transmute::<Self::Output, Self>(from)
262 }
263 #[inline]
264 fn transform_mut<F>(&'a mut self, f: F)
265 where
266 F: 'static + for<'b> FnOnce(&'b mut Self::Output),
267 {
268 // Cast away the lifetime of Self
269 // Safety: this is equivalent to f(transmute(self)), and the documentation of the trait
270 // method explains why doing so is sound.
271 #[allow(clippy::unnecessary_cast)] // false positive, lifetimes differ
272 let y = unsafe { &mut *(self as *mut Self as *mut Self::Output) };
273 f(y)
274 }
275}
276
277// Safety: &'a T is covariant in 'a.
278unsafe impl<'a, T: 'static + ?Sized> Yokeable<'a> for &'static T {
279 type Output = &'a T;
280 #[inline]
281 fn transform(&'a self) -> &'a &'a T {
282 // Doesn't need unsafe: `'a` is covariant so this lifetime cast is always safe
283 self
284 }
285 #[inline]
286 fn transform_owned(self) -> &'a T {
287 // Doesn't need unsafe: `'a` is covariant so this lifetime cast is always safe
288 self
289 }
290 #[inline]
291 unsafe fn make(from: &'a T) -> Self {
292 // Safety: function safety invariant guarantees that the returned reference
293 // will never be used beyond its original lifetime.
294 &*(from as *const T)
295 }
296 #[inline]
297 fn transform_mut<F>(&'a mut self, f: F)
298 where
299 F: 'static + for<'b> FnOnce(&'b mut Self::Output),
300 {
301 // Cast away the lifetime of Self
302 // Safety: this is equivalent to f(transmute(self)), and the documentation of the trait
303 // method explains why doing so is sound.
304 #[allow(clippy::unnecessary_cast)] // false positive, lifetimes differ
305 let y = unsafe { &mut *(self as *mut Self as *mut Self::Output) };
306 f(y)
307 }
308}
309
310#[cfg(feature = "alloc")]
311// Safety: Vec<T: 'static> never borrows.
312unsafe impl<'a, T: 'static> Yokeable<'a> for alloc::vec::Vec<T> {
313 type Output = alloc::vec::Vec<T>;
314 #[inline]
315 fn transform(&'a self) -> &'a alloc::vec::Vec<T> {
316 self
317 }
318 #[inline]
319 fn transform_owned(self) -> alloc::vec::Vec<T> {
320 self
321 }
322 #[inline]
323 unsafe fn make(from: alloc::vec::Vec<T>) -> Self {
324 from
325 }
326 #[inline]
327 fn transform_mut<F>(&'a mut self, f: F)
328 where
329 F: 'static + for<'b> FnOnce(&'b mut Self::Output),
330 {
331 f(self)
332 }
333}
334
335// Safety: PhantomData is a ZST.
336unsafe impl<'a, T: ?Sized + 'static> Yokeable<'a> for PhantomData<T> {
337 type Output = PhantomData<T>;
338
339 fn transform(&'a self) -> &'a Self::Output {
340 self
341 }
342
343 fn transform_owned(self) -> Self::Output {
344 self
345 }
346
347 unsafe fn make(from: Self::Output) -> Self {
348 from
349 }
350
351 fn transform_mut<F>(&'a mut self, f: F)
352 where
353 // be VERY CAREFUL changing this signature, it is very nuanced (see above)
354 F: 'static + for<'b> FnOnce(&'b mut Self::Output),
355 {
356 f(self)
357 }
358}