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