Coverage Report

Created: 2026-01-09 06:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/yoke-0.7.4/src/yokeable.rs
Line
Count
Source
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")]
6
use alloc::borrow::{Cow, ToOwned};
7
use 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
/// ```
96
pub 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")]
239
unsafe impl<'a, T: 'static + ToOwned + ?Sized> Yokeable<'a> for Cow<'static, T>
240
where
241
    <T as ToOwned>::Owned: Sized,
242
{
243
    type Output = Cow<'a, T>;
244
    #[inline]
245
0
    fn transform(&'a self) -> &'a Cow<'a, T> {
246
        // Doesn't need unsafe: `'a` is covariant so this lifetime cast is always safe
247
0
        self
248
0
    }
249
    #[inline]
250
0
    fn transform_owned(self) -> Cow<'a, T> {
251
        // Doesn't need unsafe: `'a` is covariant so this lifetime cast is always safe
252
0
        self
253
0
    }
254
    #[inline]
255
0
    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
0
        debug_assert!(mem::size_of::<Cow<'a, T>>() == mem::size_of::<Self>());
260
0
        let ptr: *const Self = (&from as *const Self::Output).cast();
261
0
        mem::forget(from);
262
0
        core::ptr::read(ptr)
263
0
    }
264
    #[inline]
265
0
    fn transform_mut<F>(&'a mut self, f: F)
266
0
    where
267
0
        F: 'static + for<'b> FnOnce(&'b mut Self::Output),
268
    {
269
        // Cast away the lifetime of Self
270
0
        unsafe { f(mem::transmute::<&'a mut Self, &'a mut Self::Output>(self)) }
271
0
    }
272
}
273
274
unsafe impl<'a, T: 'static + ?Sized> Yokeable<'a> for &'static T {
275
    type Output = &'a T;
276
    #[inline]
277
0
    fn transform(&'a self) -> &'a &'a T {
278
        // Doesn't need unsafe: `'a` is covariant so this lifetime cast is always safe
279
0
        self
280
0
    }
281
    #[inline]
282
0
    fn transform_owned(self) -> &'a T {
283
        // Doesn't need unsafe: `'a` is covariant so this lifetime cast is always safe
284
0
        self
285
0
    }
286
    #[inline]
287
0
    unsafe fn make(from: &'a T) -> Self {
288
0
        mem::transmute(from)
289
0
    }
Unexecuted instantiation: <&[u8] as yoke::yokeable::Yokeable>::make
Unexecuted instantiation: <&_ as yoke::yokeable::Yokeable>::make
290
    #[inline]
291
0
    fn transform_mut<F>(&'a mut self, f: F)
292
0
    where
293
0
        F: 'static + for<'b> FnOnce(&'b mut Self::Output),
294
    {
295
        // Cast away the lifetime of Self
296
0
        unsafe { f(mem::transmute::<&'a mut Self, &'a mut Self::Output>(self)) }
297
0
    }
298
}
299
300
#[cfg(feature = "alloc")]
301
unsafe impl<'a, T: 'static> Yokeable<'a> for alloc::vec::Vec<T> {
302
    type Output = alloc::vec::Vec<T>;
303
    #[inline]
304
0
    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
0
        self
307
0
    }
308
    #[inline]
309
0
    fn transform_owned(self) -> alloc::vec::Vec<T> {
310
        // Doesn't need unsafe: `'a` is covariant so this lifetime cast is always safe
311
0
        self
312
0
    }
313
    #[inline]
314
0
    unsafe fn make(from: alloc::vec::Vec<T>) -> Self {
315
0
        from
316
0
    }
317
    #[inline]
318
0
    fn transform_mut<F>(&'a mut self, f: F)
319
0
    where
320
0
        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
0
        f(self)
324
0
    }
325
}