Coverage Report

Created: 2025-10-28 06:23

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/yoke-0.8.0/src/yoke.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
use crate::cartable_ptr::{CartableOptionPointer, CartablePointerLike};
6
use crate::either::EitherCart;
7
#[cfg(feature = "alloc")]
8
use crate::erased::{ErasedArcCart, ErasedBoxCart, ErasedRcCart};
9
use crate::kinda_sorta_dangling::KindaSortaDangling;
10
use crate::Yokeable;
11
use core::marker::PhantomData;
12
use core::ops::Deref;
13
use stable_deref_trait::StableDeref;
14
15
#[cfg(feature = "alloc")]
16
use alloc::boxed::Box;
17
#[cfg(feature = "alloc")]
18
use alloc::rc::Rc;
19
#[cfg(feature = "alloc")]
20
use alloc::sync::Arc;
21
22
/// A Cow-like borrowed object "yoked" to its backing data.
23
///
24
/// This allows things like zero copy deserialized data to carry around
25
/// shared references to their backing buffer, by "erasing" their static lifetime
26
/// and turning it into a dynamically managed one.
27
///
28
/// `Y` (the [`Yokeable`]) is the object containing the references,
29
/// and will typically be of the form `Foo<'static>`. The `'static` is
30
/// not the actual lifetime of the data, rather it is a convenient way to mark the
31
/// erased lifetime and make it dynamic.
32
///
33
/// `C` is the "cart", which `Y` may contain references to. After the yoke is constructed,
34
/// the cart serves little purpose except to guarantee that `Y`'s references remain valid
35
/// for as long as the yoke remains in memory (by calling the destructor at the appropriate moment).
36
///
37
/// The primary constructor for [`Yoke`] is [`Yoke::attach_to_cart()`]. Several variants of that
38
/// constructor are provided to serve numerous types of call sites and `Yoke` signatures.
39
///
40
/// The key behind this type is [`Yoke::get()`], where calling [`.get()`][Yoke::get] on a type like
41
/// `Yoke<Cow<'static, str>, _>` will get you a short-lived `&'a Cow<'a, str>`, restricted to the
42
/// lifetime of the borrow used during `.get()`. This is entirely safe since the `Cow` borrows from
43
/// the cart type `C`, which cannot be interfered with as long as the `Yoke` is borrowed by `.get
44
/// ()`. `.get()` protects access by essentially reifying the erased lifetime to a safe local one
45
/// when necessary.
46
///
47
/// Furthermore, there are various [`.map_project()`][Yoke::map_project] methods that allow turning a `Yoke`
48
/// into another `Yoke` containing a different type that may contain elements of the original yoked
49
/// value. See the [`Yoke::map_project()`] docs for more details.
50
///
51
/// In general, `C` is a concrete type, but it is also possible for it to be a trait object.
52
///
53
/// # Example
54
///
55
/// For example, we can use this to store zero-copy deserialized data in a cache:
56
///
57
/// ```rust
58
/// # use yoke::Yoke;
59
/// # use std::rc::Rc;
60
/// # use std::borrow::Cow;
61
/// # fn load_from_cache(_filename: &str) -> Rc<[u8]> {
62
/// #     // dummy implementation
63
/// #     Rc::new([0x5, 0, 0, 0, 0, 0, 0, 0, 0x68, 0x65, 0x6c, 0x6c, 0x6f])
64
/// # }
65
///
66
/// fn load_object(filename: &str) -> Yoke<Cow<'static, str>, Rc<[u8]>> {
67
///     let rc: Rc<[u8]> = load_from_cache(filename);
68
///     Yoke::<Cow<'static, str>, Rc<[u8]>>::attach_to_cart(rc, |data: &[u8]| {
69
///         // essentially forcing a #[serde(borrow)]
70
///         Cow::Borrowed(bincode::deserialize(data).unwrap())
71
///     })
72
/// }
73
///
74
/// let yoke = load_object("filename.bincode");
75
/// assert_eq!(&**yoke.get(), "hello");
76
/// assert!(matches!(yoke.get(), &Cow::Borrowed(_)));
77
/// ```
78
pub struct Yoke<Y: for<'a> Yokeable<'a>, C> {
79
    // must be the first field for drop order
80
    // this will have a 'static lifetime parameter, that parameter is a lie
81
    yokeable: KindaSortaDangling<Y>,
82
    // Safety invariant: this type can be anything, but `yokeable` may only contain references to
83
    // StableDeref parts of this cart, and the targets of those references must be valid for the
84
    // lifetime of this cart (it must own or borrow them). It's ok for this cart to contain stack
85
    // data as long as it is not referenced by `yokeable` during construction. `attach_to_cart`,
86
    // the typical constructor of this type, upholds this invariant, but other constructors like
87
    // `replace_cart` need to uphold it.
88
    // The implementation guarantees that there are no live `yokeable`s that reference data
89
    // in a `cart` when the `cart` is dropped; this is guaranteed in the drop glue through field
90
    // order.
91
    cart: C,
92
}
93
94
// Manual `Debug` implementation, since the derived one would be unsound.
95
// See https://github.com/unicode-org/icu4x/issues/3685
96
impl<Y: for<'a> Yokeable<'a>, C: core::fmt::Debug> core::fmt::Debug for Yoke<Y, C>
97
where
98
    for<'a> <Y as Yokeable<'a>>::Output: core::fmt::Debug,
99
{
100
0
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
101
0
        f.debug_struct("Yoke")
102
0
            .field("yokeable", self.get())
103
0
            .field("cart", self.backing_cart())
104
0
            .finish()
105
0
    }
106
}
107
108
#[test]
109
fn test_debug() {
110
    let local_data = "foo".to_owned();
111
    let y1 = Yoke::<alloc::borrow::Cow<'static, str>, Rc<String>>::attach_to_zero_copy_cart(
112
        Rc::new(local_data),
113
    );
114
    assert_eq!(
115
        format!("{y1:?}"),
116
        r#"Yoke { yokeable: "foo", cart: "foo" }"#,
117
    );
118
}
119
120
impl<Y: for<'a> Yokeable<'a>, C: StableDeref> Yoke<Y, C>
121
where
122
    <C as Deref>::Target: 'static,
123
{
124
    /// Construct a [`Yoke`] by yokeing an object to a cart in a closure.
125
    ///
126
    /// The closure can read and write data outside of its scope, but data it returns
127
    /// may borrow only from the argument passed to the closure.
128
    ///
129
    /// See also [`Yoke::try_attach_to_cart()`] to return a `Result` from the closure.
130
    ///
131
    /// Call sites for this function may not compile pre-1.61; if this still happens, use
132
    /// [`Yoke::attach_to_cart_badly()`] and file a bug.
133
    ///
134
    /// # Examples
135
    ///
136
    /// ```
137
    /// # use yoke::Yoke;
138
    /// # use std::rc::Rc;
139
    /// # use std::borrow::Cow;
140
    /// # fn load_from_cache(_filename: &str) -> Rc<[u8]> {
141
    /// #     // dummy implementation
142
    /// #     Rc::new([0x5, 0, 0, 0, 0, 0, 0, 0, 0x68, 0x65, 0x6c, 0x6c, 0x6f])
143
    /// # }
144
    ///
145
    /// fn load_object(filename: &str) -> Yoke<Cow<'static, str>, Rc<[u8]>> {
146
    ///     let rc: Rc<[u8]> = load_from_cache(filename);
147
    ///     Yoke::<Cow<'static, str>, Rc<[u8]>>::attach_to_cart(rc, |data: &[u8]| {
148
    ///         // essentially forcing a #[serde(borrow)]
149
    ///         Cow::Borrowed(bincode::deserialize(data).unwrap())
150
    ///     })
151
    /// }
152
    ///
153
    /// let yoke: Yoke<Cow<str>, _> = load_object("filename.bincode");
154
    /// assert_eq!(&**yoke.get(), "hello");
155
    /// assert!(matches!(yoke.get(), &Cow::Borrowed(_)));
156
    /// ```
157
    ///
158
    /// Write the number of consumed bytes to a local variable:
159
    ///
160
    /// ```
161
    /// # use yoke::Yoke;
162
    /// # use std::rc::Rc;
163
    /// # use std::borrow::Cow;
164
    /// # fn load_from_cache(_filename: &str) -> Rc<[u8]> {
165
    /// #     // dummy implementation
166
    /// #     Rc::new([0x5, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0, 0, 0])
167
    /// # }
168
    ///
169
    /// fn load_object(
170
    ///     filename: &str,
171
    /// ) -> (Yoke<Cow<'static, str>, Rc<[u8]>>, usize) {
172
    ///     let rc: Rc<[u8]> = load_from_cache(filename);
173
    ///     let mut bytes_remaining = 0;
174
    ///     let bytes_remaining = &mut bytes_remaining;
175
    ///     let yoke = Yoke::<Cow<'static, str>, Rc<[u8]>>::attach_to_cart(
176
    ///         rc,
177
    ///         |data: &[u8]| {
178
    ///             let mut d = postcard::Deserializer::from_bytes(data);
179
    ///             let output = serde::Deserialize::deserialize(&mut d);
180
    ///             *bytes_remaining = d.finalize().unwrap().len();
181
    ///             Cow::Borrowed(output.unwrap())
182
    ///         },
183
    ///     );
184
    ///     (yoke, *bytes_remaining)
185
    /// }
186
    ///
187
    /// let (yoke, bytes_remaining) = load_object("filename.postcard");
188
    /// assert_eq!(&**yoke.get(), "hello");
189
    /// assert!(matches!(yoke.get(), &Cow::Borrowed(_)));
190
    /// assert_eq!(bytes_remaining, 3);
191
    /// ```
192
0
    pub fn attach_to_cart<F>(cart: C, f: F) -> Self
193
0
    where
194
0
        // safety note: This works by enforcing that the *only* place the return value of F
195
0
        // can borrow from is the cart, since `F` must be valid for all lifetimes `'de`
196
0
        //
197
0
        // The <C as Deref>::Target: 'static on the impl is crucial for safety as well
198
0
        //
199
0
        // See safety docs at the bottom of this file for more information
200
0
        F: for<'de> FnOnce(&'de <C as Deref>::Target) -> <Y as Yokeable<'de>>::Output,
201
0
        <C as Deref>::Target: 'static,
202
    {
203
0
        let deserialized = f(cart.deref());
204
0
        Self {
205
0
            yokeable: KindaSortaDangling::new(
206
0
                // Safety: the resulting `yokeable` is dropped before the `cart` because
207
0
                // of the Yoke invariant. See the safety docs at the bottom of this file
208
0
                // for the justification of why yokeable could only borrow from the Cart.
209
0
                unsafe { Y::make(deserialized) },
210
0
            ),
211
0
            cart,
212
0
        }
213
0
    }
214
215
    /// Construct a [`Yoke`] by yokeing an object to a cart. If an error occurs in the
216
    /// deserializer function, the error is passed up to the caller.
217
    ///
218
    /// Call sites for this function may not compile pre-1.61; if this still happens, use
219
    /// [`Yoke::try_attach_to_cart_badly()`] and file a bug.
220
0
    pub fn try_attach_to_cart<E, F>(cart: C, f: F) -> Result<Self, E>
221
0
    where
222
0
        F: for<'de> FnOnce(&'de <C as Deref>::Target) -> Result<<Y as Yokeable<'de>>::Output, E>,
223
0
        <C as Deref>::Target: 'static,
224
    {
225
0
        let deserialized = f(cart.deref())?;
226
0
        Ok(Self {
227
0
            yokeable: KindaSortaDangling::new(
228
0
                // Safety: the resulting `yokeable` is dropped before the `cart` because
229
0
                // of the Yoke invariant. See the safety docs at the bottom of this file
230
0
                // for the justification of why yokeable could only borrow from the Cart.
231
0
                unsafe { Y::make(deserialized) },
232
0
            ),
233
0
            cart,
234
0
        })
235
0
    }
236
237
    /// Use [`Yoke::attach_to_cart()`].
238
    ///
239
    /// This was needed because the pre-1.61 compiler couldn't always handle the FnOnce trait bound.
240
    #[deprecated]
241
0
    pub fn attach_to_cart_badly(
242
0
        cart: C,
243
0
        f: for<'de> fn(&'de <C as Deref>::Target) -> <Y as Yokeable<'de>>::Output,
244
0
    ) -> Self {
245
0
        Self::attach_to_cart(cart, f)
246
0
    }
247
248
    /// Use [`Yoke::try_attach_to_cart()`].
249
    ///
250
    /// This was needed because the pre-1.61 compiler couldn't always handle the FnOnce trait bound.
251
    #[deprecated]
252
0
    pub fn try_attach_to_cart_badly<E>(
253
0
        cart: C,
254
0
        f: for<'de> fn(&'de <C as Deref>::Target) -> Result<<Y as Yokeable<'de>>::Output, E>,
255
0
    ) -> Result<Self, E> {
256
0
        Self::try_attach_to_cart(cart, f)
257
0
    }
258
}
259
260
impl<Y: for<'a> Yokeable<'a>, C> Yoke<Y, C> {
261
    /// Obtain a valid reference to the yokeable data
262
    ///
263
    /// This essentially transforms the lifetime of the internal yokeable data to
264
    /// be valid.
265
    /// For example, if you're working with a `Yoke<Cow<'static, T>, C>`, this
266
    /// will return an `&'a Cow<'a, T>`
267
    ///
268
    /// # Example
269
    ///
270
    /// ```rust
271
    /// # use yoke::Yoke;
272
    /// # use std::rc::Rc;
273
    /// # use std::borrow::Cow;
274
    /// # fn load_from_cache(_filename: &str) -> Rc<[u8]> {
275
    /// #     // dummy implementation
276
    /// #     Rc::new([0x5, 0, 0, 0, 0, 0, 0, 0, 0x68, 0x65, 0x6c, 0x6c, 0x6f])
277
    /// # }
278
    /// #
279
    /// # fn load_object(filename: &str) -> Yoke<Cow<'static, str>, Rc<[u8]>> {
280
    /// #     let rc: Rc<[u8]> = load_from_cache(filename);
281
    /// #     Yoke::<Cow<'static, str>, Rc<[u8]>>::attach_to_cart(rc, |data: &[u8]| {
282
    /// #         Cow::Borrowed(bincode::deserialize(data).unwrap())
283
    /// #     })
284
    /// # }
285
    ///
286
    /// // load_object() defined in the example at the top of this page
287
    /// let yoke: Yoke<Cow<str>, _> = load_object("filename.bincode");
288
    /// assert_eq!(yoke.get(), "hello");
289
    /// ```
290
    #[inline]
291
0
    pub fn get<'a>(&'a self) -> &'a <Y as Yokeable<'a>>::Output {
292
0
        self.yokeable.transform()
293
0
    }
Unexecuted instantiation: <yoke::yoke::Yoke<icu_properties::provider::PropertyUnicodeSet, yoke::cartable_ptr::CartableOptionPointer<&()>>>::get
Unexecuted instantiation: <yoke::yoke::Yoke<icu_properties::provider::PropertyCodePointSet, yoke::cartable_ptr::CartableOptionPointer<&()>>>::get
Unexecuted instantiation: <yoke::yoke::Yoke<icu_normalizer::provider::DecompositionData, yoke::cartable_ptr::CartableOptionPointer<&()>>>::get
Unexecuted instantiation: <yoke::yoke::Yoke<icu_normalizer::provider::DecompositionTables, yoke::cartable_ptr::CartableOptionPointer<&()>>>::get
Unexecuted instantiation: <yoke::yoke::Yoke<icu_normalizer::provider::CanonicalCompositions, yoke::cartable_ptr::CartableOptionPointer<&()>>>::get
Unexecuted instantiation: <yoke::yoke::Yoke<icu_normalizer::provider::NonRecursiveDecompositionSupplement, yoke::cartable_ptr::CartableOptionPointer<&()>>>::get
Unexecuted instantiation: <yoke::yoke::Yoke<_, _>>::get
294
295
    /// Get a reference to the backing cart.
296
    ///
297
    /// This can be useful when building caches, etc. However, if you plan to store the cart
298
    /// separately from the yoke, read the note of caution below in [`Yoke::into_backing_cart`].
299
0
    pub fn backing_cart(&self) -> &C {
300
0
        &self.cart
301
0
    }
302
303
    /// Get the backing cart by value, dropping the yokeable object.
304
    ///
305
    /// **Caution:** Calling this method could cause information saved in the yokeable object but
306
    /// not the cart to be lost. Use this method only if the yokeable object cannot contain its
307
    /// own information.
308
    ///
309
    /// # Example
310
    ///
311
    /// Good example: the yokeable object is only a reference, so no information can be lost.
312
    ///
313
    /// ```
314
    /// use yoke::Yoke;
315
    ///
316
    /// let local_data = "foo".to_owned();
317
    /// let yoke = Yoke::<&'static str, Box<String>>::attach_to_zero_copy_cart(
318
    ///     Box::new(local_data),
319
    /// );
320
    /// assert_eq!(*yoke.get(), "foo");
321
    ///
322
    /// // Get back the cart
323
    /// let cart = yoke.into_backing_cart();
324
    /// assert_eq!(&*cart, "foo");
325
    /// ```
326
    ///
327
    /// Bad example: information specified in `.with_mut()` is lost.
328
    ///
329
    /// ```
330
    /// use std::borrow::Cow;
331
    /// use yoke::Yoke;
332
    ///
333
    /// let local_data = "foo".to_owned();
334
    /// let mut yoke =
335
    ///     Yoke::<Cow<'static, str>, Box<String>>::attach_to_zero_copy_cart(
336
    ///         Box::new(local_data),
337
    ///     );
338
    /// assert_eq!(yoke.get(), "foo");
339
    ///
340
    /// // Override data in the cart
341
    /// yoke.with_mut(|cow| {
342
    ///     let mut_str = cow.to_mut();
343
    ///     mut_str.clear();
344
    ///     mut_str.push_str("bar");
345
    /// });
346
    /// assert_eq!(yoke.get(), "bar");
347
    ///
348
    /// // Get back the cart
349
    /// let cart = yoke.into_backing_cart();
350
    /// assert_eq!(&*cart, "foo"); // WHOOPS!
351
    /// ```
352
0
    pub fn into_backing_cart(self) -> C {
353
0
        self.cart
354
0
    }
355
356
    /// Unsafe function for replacing the cart with another
357
    ///
358
    /// This can be used for type-erasing the cart, for example.
359
    ///
360
    /// # Safety
361
    ///
362
    /// - `f()` must not panic
363
    /// - References from the yokeable `Y` should still be valid for the lifetime of the
364
    ///   returned cart type `C`.
365
    ///
366
    ///   For the purpose of determining this, `Yoke` guarantees that references from the Yokeable
367
    ///   `Y` into the cart `C` will never be references into its stack data, only heap data protected
368
    ///   by `StableDeref`. This does not necessarily mean that `C` implements `StableDeref`, rather that
369
    ///   any data referenced by `Y` must be accessed through a `StableDeref` impl on something `C` owns.
370
    ///
371
    ///   Concretely, this means that if `C = Option<Rc<T>>`, `Y` may contain references to the `T` but not
372
    ///   anything else.
373
    /// - Lifetimes inside C must not be lengthened, even if they are themselves contravariant.
374
    ///   I.e., if C contains an `fn(&'a u8)`, it cannot be replaced with `fn(&'static u8),
375
    ///   even though that is typically safe.
376
    ///
377
    /// Typically, this means implementing `f` as something which _wraps_ the inner cart type `C`.
378
    /// `Yoke` only really cares about destructors for its carts so it's fine to erase other
379
    /// information about the cart, as long as the backing data will still be destroyed at the
380
    /// same time.
381
    #[inline]
382
0
    pub unsafe fn replace_cart<C2>(self, f: impl FnOnce(C) -> C2) -> Yoke<Y, C2> {
383
0
        Yoke {
384
0
            // Safety note: the safety invariant of this function guarantees that
385
0
            // the data that the yokeable references has its ownership (if any)
386
0
            // transferred to the new cart before self.cart is dropped.
387
0
            yokeable: self.yokeable,
388
0
            cart: f(self.cart),
389
0
        }
390
0
    }
Unexecuted instantiation: <yoke::yoke::Yoke<&[u8], core::option::Option<icu_provider::response::Cart>>>::replace_cart::<core::option::Option<&()>, <icu_provider::response::Cart>::unwrap_cart<&[u8]>::{closure#0}>
Unexecuted instantiation: <yoke::yoke::Yoke<_, _>>::replace_cart::<_, _>
391
392
    /// Mutate the stored [`Yokeable`] data.
393
    ///
394
    /// See [`Yokeable::transform_mut()`] for why this operation is safe.
395
    ///
396
    /// # Example
397
    ///
398
    /// This can be used to partially mutate the stored data, provided
399
    /// no _new_ borrowed data is introduced.
400
    ///
401
    /// ```rust
402
    /// # use yoke::{Yoke, Yokeable};
403
    /// # use std::rc::Rc;
404
    /// # use std::borrow::Cow;
405
    /// # use std::mem;
406
    /// # fn load_from_cache(_filename: &str) -> Rc<[u8]> {
407
    /// #     // dummy implementation
408
    /// #     Rc::new([0x5, 0, 0, 0, 0, 0, 0, 0, 0x68, 0x65, 0x6c, 0x6c, 0x6f])
409
    /// # }
410
    /// #
411
    /// # fn load_object(filename: &str) -> Yoke<Bar<'static>, Rc<[u8]>> {
412
    /// #     let rc: Rc<[u8]> = load_from_cache(filename);
413
    /// #     Yoke::<Bar<'static>, Rc<[u8]>>::attach_to_cart(rc, |data: &[u8]| {
414
    /// #         // A real implementation would properly deserialize `Bar` as a whole
415
    /// #         Bar {
416
    /// #             numbers: Cow::Borrowed(bincode::deserialize(data).unwrap()),
417
    /// #             string: Cow::Borrowed(bincode::deserialize(data).unwrap()),
418
    /// #             owned: Vec::new(),
419
    /// #         }
420
    /// #     })
421
    /// # }
422
    ///
423
    /// // also implements Yokeable
424
    /// struct Bar<'a> {
425
    ///     numbers: Cow<'a, [u8]>,
426
    ///     string: Cow<'a, str>,
427
    ///     owned: Vec<u8>,
428
    /// }
429
    ///
430
    /// // `load_object()` deserializes an object from a file
431
    /// let mut bar: Yoke<Bar, _> = load_object("filename.bincode");
432
    /// assert_eq!(bar.get().string, "hello");
433
    /// assert!(matches!(bar.get().string, Cow::Borrowed(_)));
434
    /// assert_eq!(&*bar.get().numbers, &[0x68, 0x65, 0x6c, 0x6c, 0x6f]);
435
    /// assert!(matches!(bar.get().numbers, Cow::Borrowed(_)));
436
    /// assert_eq!(&*bar.get().owned, &[]);
437
    ///
438
    /// bar.with_mut(|bar| {
439
    ///     bar.string.to_mut().push_str(" world");
440
    ///     bar.owned.extend_from_slice(&[1, 4, 1, 5, 9]);
441
    /// });
442
    ///
443
    /// assert_eq!(bar.get().string, "hello world");
444
    /// assert!(matches!(bar.get().string, Cow::Owned(_)));
445
    /// assert_eq!(&*bar.get().owned, &[1, 4, 1, 5, 9]);
446
    /// // Unchanged and still Cow::Borrowed
447
    /// assert_eq!(&*bar.get().numbers, &[0x68, 0x65, 0x6c, 0x6c, 0x6f]);
448
    /// assert!(matches!(bar.get().numbers, Cow::Borrowed(_)));
449
    ///
450
    /// # unsafe impl<'a> Yokeable<'a> for Bar<'static> {
451
    /// #     type Output = Bar<'a>;
452
    /// #     fn transform(&'a self) -> &'a Bar<'a> {
453
    /// #         self
454
    /// #     }
455
    /// #
456
    /// #     fn transform_owned(self) -> Bar<'a> {
457
    /// #         // covariant lifetime cast, can be done safely
458
    /// #         self
459
    /// #     }
460
    /// #
461
    /// #     unsafe fn make(from: Bar<'a>) -> Self {
462
    /// #         let ret = mem::transmute_copy(&from);
463
    /// #         mem::forget(from);
464
    /// #         ret
465
    /// #     }
466
    /// #
467
    /// #     fn transform_mut<F>(&'a mut self, f: F)
468
    /// #     where
469
    /// #         F: 'static + FnOnce(&'a mut Self::Output),
470
    /// #     {
471
    /// #         unsafe { f(mem::transmute(self)) }
472
    /// #     }
473
    /// # }
474
    /// ```
475
0
    pub fn with_mut<'a, F>(&'a mut self, f: F)
476
0
    where
477
0
        F: 'static + for<'b> FnOnce(&'b mut <Y as Yokeable<'a>>::Output),
478
    {
479
0
        self.yokeable.transform_mut(f)
480
0
    }
481
482
    /// Helper function allowing one to wrap the cart type `C` in an `Option<T>`.
483
    #[inline]
484
0
    pub fn wrap_cart_in_option(self) -> Yoke<Y, Option<C>> {
485
        // Safety: the cart is preserved (since it is just wrapped into a Some),
486
        // so any data it owns is too.
487
0
        unsafe { self.replace_cart(Some) }
488
0
    }
489
}
490
491
impl<Y: for<'a> Yokeable<'a>> Yoke<Y, ()> {
492
    /// Construct a new [`Yoke`] from static data. There will be no
493
    /// references to `cart` here since [`Yokeable`]s are `'static`,
494
    /// this is good for e.g. constructing fully owned
495
    /// [`Yoke`]s with no internal borrowing.
496
    ///
497
    /// This is similar to [`Yoke::new_owned()`] but it does not allow you to
498
    /// mix the [`Yoke`] with borrowed data. This is primarily useful
499
    /// for using [`Yoke`] in generic scenarios.
500
    ///
501
    /// # Example
502
    ///
503
    /// ```rust
504
    /// # use yoke::Yoke;
505
    /// # use std::borrow::Cow;
506
    ///
507
    /// let owned: Cow<str> = "hello".to_owned().into();
508
    /// // this yoke can be intermingled with actually-borrowed Yokes
509
    /// let yoke: Yoke<Cow<str>, ()> = Yoke::new_always_owned(owned);
510
    ///
511
    /// assert_eq!(yoke.get(), "hello");
512
    /// ```
513
0
    pub fn new_always_owned(yokeable: Y) -> Self {
514
0
        Self {
515
0
            // Safety note: this `yokeable` certainly does not reference data owned by (), so we do
516
0
            // not have to worry about when the `yokeable` is dropped.
517
0
            yokeable: KindaSortaDangling::new(yokeable),
518
0
            cart: (),
519
0
        }
520
0
    }
521
522
    /// Obtain the yokeable out of a `Yoke<Y, ()>`
523
    ///
524
    /// For most `Yoke` types this would be unsafe but it's
525
    /// fine for `Yoke<Y, ()>` since there are no actual internal
526
    /// references
527
0
    pub fn into_yokeable(self) -> Y {
528
        // Safety note: since `yokeable` cannot reference data owned by `()`, this is certainly
529
        // safe.
530
0
        self.yokeable.into_inner()
531
0
    }
532
}
533
534
// C does not need to be StableDeref here, if the yoke was constructed it's valid,
535
// and new_owned() doesn't construct a yokeable that uses references,
536
impl<Y: for<'a> Yokeable<'a>, C> Yoke<Y, Option<C>> {
537
    /// Construct a new [`Yoke`] from static data. There will be no
538
    /// references to `cart` here since [`Yokeable`]s are `'static`,
539
    /// this is good for e.g. constructing fully owned
540
    /// [`Yoke`]s with no internal borrowing.
541
    ///
542
    /// This can be paired with [`Yoke:: wrap_cart_in_option()`] to mix owned
543
    /// and borrowed data.
544
    ///
545
    /// If you do not wish to pair this with borrowed data, [`Yoke::new_always_owned()`] can
546
    /// be used to get a [`Yoke`] API on always-owned data.
547
    ///
548
    /// # Example
549
    ///
550
    /// ```rust
551
    /// # use yoke::Yoke;
552
    /// # use std::borrow::Cow;
553
    /// # use std::rc::Rc;
554
    ///
555
    /// let owned: Cow<str> = "hello".to_owned().into();
556
    /// // this yoke can be intermingled with actually-borrowed Yokes
557
    /// let yoke: Yoke<Cow<str>, Option<Rc<[u8]>>> = Yoke::new_owned(owned);
558
    ///
559
    /// assert_eq!(yoke.get(), "hello");
560
    /// ```
561
0
    pub const fn new_owned(yokeable: Y) -> Self {
562
0
        Self {
563
0
            // Safety note: this `yokeable` is known not to borrow from the cart.
564
0
            yokeable: KindaSortaDangling::new(yokeable),
565
0
            cart: None,
566
0
        }
567
0
    }
Unexecuted instantiation: <yoke::yoke::Yoke<icu_properties::provider::PropertyUnicodeSet, core::option::Option<&()>>>::new_owned
Unexecuted instantiation: <yoke::yoke::Yoke<icu_properties::provider::PropertyCodePointSet, core::option::Option<&()>>>::new_owned
Unexecuted instantiation: <yoke::yoke::Yoke<&[u8], core::option::Option<&()>>>::new_owned
Unexecuted instantiation: <yoke::yoke::Yoke<_, core::option::Option<_>>>::new_owned
568
569
    /// Obtain the yokeable out of a `Yoke<Y, Option<C>>` if possible.
570
    ///
571
    /// If the cart is `None`, this returns `Ok`, but if the cart is `Some`,
572
    /// this returns `self` as an error.
573
0
    pub fn try_into_yokeable(self) -> Result<Y, Self> {
574
        // Safety: if the cart is None there is no way for the yokeable to
575
        // have references into it because of the cart invariant.
576
0
        match self.cart {
577
0
            Some(_) => Err(self),
578
0
            None => Ok(self.yokeable.into_inner()),
579
        }
580
0
    }
581
}
582
583
impl<Y: for<'a> Yokeable<'a>, C: CartablePointerLike> Yoke<Y, Option<C>> {
584
    /// Converts a `Yoke<Y, Option<C>>` to `Yoke<Y, CartableOptionPointer<C>>`
585
    /// for better niche optimization when stored as a field.
586
    ///
587
    /// # Examples
588
    ///
589
    /// ```
590
    /// use std::borrow::Cow;
591
    /// use yoke::Yoke;
592
    ///
593
    /// let yoke: Yoke<Cow<[u8]>, Box<Vec<u8>>> =
594
    ///     Yoke::attach_to_cart(vec![10, 20, 30].into(), |c| c.into());
595
    ///
596
    /// let yoke_option = yoke.wrap_cart_in_option();
597
    /// let yoke_option_pointer = yoke_option.convert_cart_into_option_pointer();
598
    /// ```
599
    ///
600
    /// The niche improves stack sizes:
601
    ///
602
    /// ```
603
    /// use yoke::Yoke;
604
    /// use yoke::cartable_ptr::CartableOptionPointer;
605
    /// use std::mem::size_of;
606
    /// use std::rc::Rc;
607
    ///
608
    /// // The data struct is 6 words:
609
    /// # #[derive(yoke::Yokeable)]
610
    /// # struct MyDataStruct<'a> {
611
    /// #     _s: (usize, usize, usize, usize),
612
    /// #     _p: &'a str,
613
    /// # }
614
    /// const W: usize = core::mem::size_of::<usize>();
615
    /// assert_eq!(W * 6, size_of::<MyDataStruct>());
616
    ///
617
    /// // An enum containing the data struct with an `Option<Rc>` cart is 8 words:
618
    /// enum StaticOrYoke1 {
619
    ///     Static(&'static MyDataStruct<'static>),
620
    ///     Yoke(Yoke<MyDataStruct<'static>, Option<Rc<String>>>),
621
    /// }
622
    /// assert_eq!(W * 8, size_of::<StaticOrYoke1>());
623
    ///
624
    /// // When using `CartableOptionPointer``, we need only 7 words for the same behavior:
625
    /// enum StaticOrYoke2 {
626
    ///     Static(&'static MyDataStruct<'static>),
627
    ///     Yoke(Yoke<MyDataStruct<'static>, CartableOptionPointer<Rc<String>>>),
628
    /// }
629
    /// assert_eq!(W * 7, size_of::<StaticOrYoke2>());
630
    /// ```
631
    #[inline]
632
0
    pub fn convert_cart_into_option_pointer(self) -> Yoke<Y, CartableOptionPointer<C>> {
633
0
        match self.cart {
634
0
            Some(cart) => Yoke {
635
0
                // Safety note: CartableOptionPointer::from_cartable only wraps the `cart`,
636
0
                // so the data referenced by the yokeable is still live.
637
0
                yokeable: self.yokeable,
638
0
                cart: CartableOptionPointer::from_cartable(cart),
639
0
            },
640
0
            None => Yoke {
641
0
                // Safety note: this Yokeable cannot refer to any data since self.cart is None.
642
0
                yokeable: self.yokeable,
643
0
                cart: CartableOptionPointer::none(),
644
0
            },
645
        }
646
0
    }
Unexecuted instantiation: <yoke::yoke::Yoke<icu_properties::provider::PropertyUnicodeSet, core::option::Option<&()>>>::convert_cart_into_option_pointer
Unexecuted instantiation: <yoke::yoke::Yoke<icu_properties::provider::PropertyCodePointSet, core::option::Option<&()>>>::convert_cart_into_option_pointer
Unexecuted instantiation: <yoke::yoke::Yoke<&[u8], core::option::Option<&()>>>::convert_cart_into_option_pointer
Unexecuted instantiation: <yoke::yoke::Yoke<_, core::option::Option<_>>>::convert_cart_into_option_pointer
647
}
648
649
impl<Y: for<'a> Yokeable<'a>, C: CartablePointerLike> Yoke<Y, CartableOptionPointer<C>> {
650
    /// Obtain the yokeable out of a `Yoke<Y, CartableOptionPointer<C>>` if possible.
651
    ///
652
    /// If the cart is `None`, this returns `Ok`, but if the cart is `Some`,
653
    /// this returns `self` as an error.
654
    #[inline]
655
0
    pub fn try_into_yokeable(self) -> Result<Y, Self> {
656
0
        if self.cart.is_none() {
657
0
            Ok(self.yokeable.into_inner())
658
        } else {
659
0
            Err(self)
660
        }
661
0
    }
662
}
663
664
/// This trait marks cart types that do not change source on cloning
665
///
666
/// This is conceptually similar to [`stable_deref_trait::CloneStableDeref`],
667
/// however [`stable_deref_trait::CloneStableDeref`] is not (and should not) be
668
/// implemented on [`Option`] (since it's not [`Deref`]). [`CloneableCart`] essentially is
669
/// "if there _is_ data to borrow from here, cloning the cart gives you an additional
670
/// handle to the same data".
671
///
672
/// # Safety
673
/// This trait is safe to implement on `StableDeref` types which, once `Clone`d, point to the same underlying data and retain ownership.
674
///
675
/// This trait can also be implemented on aggregates of such types like `Option<T: CloneableCart>` and `(T: CloneableCart, U: CloneableCart)`.
676
///
677
/// Essentially, all data that could be referenced by a Yokeable (i.e. data that is referenced via a StableDeref) must retain the same
678
/// pointer and ownership semantics once cloned.
679
pub unsafe trait CloneableCart: Clone {}
680
681
#[cfg(feature = "alloc")]
682
// Safety: Rc<T> implements CloneStableDeref.
683
unsafe impl<T: ?Sized> CloneableCart for Rc<T> {}
684
#[cfg(feature = "alloc")]
685
// Safety: Arc<T> implements CloneStableDeref.
686
unsafe impl<T: ?Sized> CloneableCart for Arc<T> {}
687
// Safety: Option<T> cannot deref to anything that T doesn't already deref to.
688
unsafe impl<T: CloneableCart> CloneableCart for Option<T> {}
689
// Safety: &'a T is indeed StableDeref, and cloning it refers to the same data.
690
// &'a T does not own in the first place, so ownership is preserved.
691
unsafe impl<'a, T: ?Sized> CloneableCart for &'a T {}
692
// Safety: () cannot deref to anything.
693
unsafe impl CloneableCart for () {}
694
695
/// Clone requires that the cart type `C` derefs to the same address after it is cloned. This works for
696
/// Rc, Arc, and &'a T.
697
///
698
/// For other cart types, clone `.backing_cart()` and re-use `.attach_to_cart()`; however, doing
699
/// so may lose mutations performed via `.with_mut()`.
700
///
701
/// Cloning a `Yoke` is often a cheap operation requiring no heap allocations, in much the same
702
/// way that cloning an `Rc` is a cheap operation. However, if the `yokeable` contains owned data
703
/// (e.g., from `.with_mut()`), that data will need to be cloned.
704
impl<Y: for<'a> Yokeable<'a>, C: CloneableCart> Clone for Yoke<Y, C>
705
where
706
    for<'a> <Y as Yokeable<'a>>::Output: Clone,
707
{
708
0
    fn clone(&self) -> Self {
709
        // We have an &T not a T, and we can clone T
710
0
        let this = self.get().clone();
711
0
        Yoke {
712
0
            yokeable: KindaSortaDangling::new(
713
0
                // Safety: C being a CloneableCart guarantees that the data referenced by the
714
0
                // `yokeable` is kept alive by the clone of the cart.
715
0
                unsafe { Y::make(this) },
716
0
            ),
717
0
            cart: self.cart.clone(),
718
0
        }
719
0
    }
720
}
721
722
#[test]
723
fn test_clone() {
724
    let local_data = "foo".to_owned();
725
    let y1 = Yoke::<alloc::borrow::Cow<'static, str>, Rc<String>>::attach_to_zero_copy_cart(
726
        Rc::new(local_data),
727
    );
728
729
    // Test basic clone
730
    let y2 = y1.clone();
731
    assert_eq!(y1.get(), "foo");
732
    assert_eq!(y2.get(), "foo");
733
734
    // Test clone with mutation on target
735
    let mut y3 = y1.clone();
736
    y3.with_mut(|y| {
737
        y.to_mut().push_str("bar");
738
    });
739
    assert_eq!(y1.get(), "foo");
740
    assert_eq!(y2.get(), "foo");
741
    assert_eq!(y3.get(), "foobar");
742
743
    // Test that mutations on source do not affect target
744
    let y4 = y3.clone();
745
    y3.with_mut(|y| {
746
        y.to_mut().push_str("baz");
747
    });
748
    assert_eq!(y1.get(), "foo");
749
    assert_eq!(y2.get(), "foo");
750
    assert_eq!(y3.get(), "foobarbaz");
751
    assert_eq!(y4.get(), "foobar");
752
}
753
754
impl<Y: for<'a> Yokeable<'a>, C> Yoke<Y, C> {
755
    /// Allows one to "project" a yoke to perform a transformation on the data, potentially
756
    /// looking at a subfield, and producing a new yoke. This will move cart, and the provided
757
    /// transformation is only allowed to use data known to be borrowed from the cart.
758
    ///
759
    /// The callback takes an additional `PhantomData<&()>` parameter to anchor lifetimes
760
    /// (see [#86702](https://github.com/rust-lang/rust/issues/86702)) This parameter
761
    /// should just be ignored in the callback.
762
    ///
763
    /// This can be used, for example, to transform data from one format to another:
764
    ///
765
    /// ```
766
    /// # use std::rc::Rc;
767
    /// # use yoke::Yoke;
768
    /// #
769
    /// fn slice(y: Yoke<&'static str, Rc<[u8]>>) -> Yoke<&'static [u8], Rc<[u8]>> {
770
    ///     y.map_project(move |yk, _| yk.as_bytes())
771
    /// }
772
    /// ```
773
    ///
774
    /// This can also be used to create a yoke for a subfield
775
    ///
776
    /// ```
777
    /// # use yoke::{Yoke, Yokeable};
778
    /// # use std::mem;
779
    /// # use std::rc::Rc;
780
    /// #
781
    /// // also safely implements Yokeable<'a>
782
    /// struct Bar<'a> {
783
    ///     string_1: &'a str,
784
    ///     string_2: &'a str,
785
    /// }
786
    ///
787
    /// fn map_project_string_1(
788
    ///     bar: Yoke<Bar<'static>, Rc<[u8]>>,
789
    /// ) -> Yoke<&'static str, Rc<[u8]>> {
790
    ///     bar.map_project(|bar, _| bar.string_1)
791
    /// }
792
    ///
793
    /// #
794
    /// # unsafe impl<'a> Yokeable<'a> for Bar<'static> {
795
    /// #     type Output = Bar<'a>;
796
    /// #     fn transform(&'a self) -> &'a Bar<'a> {
797
    /// #         self
798
    /// #     }
799
    /// #
800
    /// #     fn transform_owned(self) -> Bar<'a> {
801
    /// #         // covariant lifetime cast, can be done safely
802
    /// #         self
803
    /// #     }
804
    /// #
805
    /// #     unsafe fn make(from: Bar<'a>) -> Self {
806
    /// #         let ret = mem::transmute_copy(&from);
807
    /// #         mem::forget(from);
808
    /// #         ret
809
    /// #     }
810
    /// #
811
    /// #     fn transform_mut<F>(&'a mut self, f: F)
812
    /// #     where
813
    /// #         F: 'static + FnOnce(&'a mut Self::Output),
814
    /// #     {
815
    /// #         unsafe { f(mem::transmute(self)) }
816
    /// #     }
817
    /// # }
818
    /// ```
819
    //
820
    // Safety docs can be found at the end of the file.
821
0
    pub fn map_project<P, F>(self, f: F) -> Yoke<P, C>
822
0
    where
823
0
        P: for<'a> Yokeable<'a>,
824
0
        F: for<'a> FnOnce(
825
0
            <Y as Yokeable<'a>>::Output,
826
0
            PhantomData<&'a ()>,
827
0
        ) -> <P as Yokeable<'a>>::Output,
828
    {
829
0
        let p = f(self.yokeable.into_inner().transform_owned(), PhantomData);
830
0
        Yoke {
831
0
            yokeable: KindaSortaDangling::new(
832
0
                // Safety: the resulting `yokeable` is dropped before the `cart` because
833
0
                // of the Yoke invariant. See the safety docs below for the justification of why
834
0
                // yokeable could only borrow from the Cart.
835
0
                unsafe { P::make(p) },
836
0
            ),
837
0
            cart: self.cart,
838
0
        }
839
0
    }
840
841
    /// This is similar to [`Yoke::map_project`], however it does not move
842
    /// [`Self`] and instead clones the cart (only if the cart is a [`CloneableCart`])
843
    ///
844
    /// This is a bit more efficient than cloning the [`Yoke`] and then calling [`Yoke::map_project`]
845
    /// because then it will not clone fields that are going to be discarded.
846
0
    pub fn map_project_cloned<'this, P, F>(&'this self, f: F) -> Yoke<P, C>
847
0
    where
848
0
        P: for<'a> Yokeable<'a>,
849
0
        C: CloneableCart,
850
0
        F: for<'a> FnOnce(
851
0
            &'this <Y as Yokeable<'a>>::Output,
852
0
            PhantomData<&'a ()>,
853
0
        ) -> <P as Yokeable<'a>>::Output,
854
    {
855
0
        let p = f(self.get(), PhantomData);
856
0
        Yoke {
857
0
            yokeable: KindaSortaDangling::new(
858
0
                // Safety: the resulting `yokeable` is dropped before the `cart` because
859
0
                // of the Yoke invariant. See the safety docs below for the justification of why
860
0
                // yokeable could only borrow from the Cart.
861
0
                unsafe { P::make(p) },
862
0
            ),
863
0
            cart: self.cart.clone(),
864
0
        }
865
0
    }
866
867
    /// This is similar to [`Yoke::map_project`], however it can also bubble up an error
868
    /// from the callback.
869
    ///
870
    /// ```
871
    /// # use std::rc::Rc;
872
    /// # use yoke::Yoke;
873
    /// # use std::str::{self, Utf8Error};
874
    /// #
875
    /// fn slice(
876
    ///     y: Yoke<&'static [u8], Rc<[u8]>>,
877
    /// ) -> Result<Yoke<&'static str, Rc<[u8]>>, Utf8Error> {
878
    ///     y.try_map_project(move |bytes, _| str::from_utf8(bytes))
879
    /// }
880
    /// ```
881
    ///
882
    /// This can also be used to create a yoke for a subfield
883
    ///
884
    /// ```
885
    /// # use yoke::{Yoke, Yokeable};
886
    /// # use std::mem;
887
    /// # use std::rc::Rc;
888
    /// # use std::str::{self, Utf8Error};
889
    /// #
890
    /// // also safely implements Yokeable<'a>
891
    /// struct Bar<'a> {
892
    ///     bytes_1: &'a [u8],
893
    ///     string_2: &'a str,
894
    /// }
895
    ///
896
    /// fn map_project_string_1(
897
    ///     bar: Yoke<Bar<'static>, Rc<[u8]>>,
898
    /// ) -> Result<Yoke<&'static str, Rc<[u8]>>, Utf8Error> {
899
    ///     bar.try_map_project(|bar, _| str::from_utf8(bar.bytes_1))
900
    /// }
901
    ///
902
    /// #
903
    /// # unsafe impl<'a> Yokeable<'a> for Bar<'static> {
904
    /// #     type Output = Bar<'a>;
905
    /// #     fn transform(&'a self) -> &'a Bar<'a> {
906
    /// #         self
907
    /// #     }
908
    /// #
909
    /// #     fn transform_owned(self) -> Bar<'a> {
910
    /// #         // covariant lifetime cast, can be done safely
911
    /// #         self
912
    /// #     }
913
    /// #
914
    /// #     unsafe fn make(from: Bar<'a>) -> Self {
915
    /// #         let ret = mem::transmute_copy(&from);
916
    /// #         mem::forget(from);
917
    /// #         ret
918
    /// #     }
919
    /// #
920
    /// #     fn transform_mut<F>(&'a mut self, f: F)
921
    /// #     where
922
    /// #         F: 'static + FnOnce(&'a mut Self::Output),
923
    /// #     {
924
    /// #         unsafe { f(mem::transmute(self)) }
925
    /// #     }
926
    /// # }
927
    /// ```
928
0
    pub fn try_map_project<P, F, E>(self, f: F) -> Result<Yoke<P, C>, E>
929
0
    where
930
0
        P: for<'a> Yokeable<'a>,
931
0
        F: for<'a> FnOnce(
932
0
            <Y as Yokeable<'a>>::Output,
933
0
            PhantomData<&'a ()>,
934
0
        ) -> Result<<P as Yokeable<'a>>::Output, E>,
935
    {
936
0
        let p = f(self.yokeable.into_inner().transform_owned(), PhantomData)?;
937
0
        Ok(Yoke {
938
0
            yokeable: KindaSortaDangling::new(
939
0
                // Safety: the resulting `yokeable` is dropped before the `cart` because
940
0
                // of the Yoke invariant. See the safety docs below for the justification of why
941
0
                // yokeable could only borrow from the Cart.
942
0
                unsafe { P::make(p) },
943
0
            ),
944
0
            cart: self.cart,
945
0
        })
946
0
    }
947
948
    /// This is similar to [`Yoke::try_map_project`], however it does not move
949
    /// [`Self`] and instead clones the cart (only if the cart is a [`CloneableCart`])
950
    ///
951
    /// This is a bit more efficient than cloning the [`Yoke`] and then calling [`Yoke::map_project`]
952
    /// because then it will not clone fields that are going to be discarded.
953
0
    pub fn try_map_project_cloned<'this, P, F, E>(&'this self, f: F) -> Result<Yoke<P, C>, E>
954
0
    where
955
0
        P: for<'a> Yokeable<'a>,
956
0
        C: CloneableCart,
957
0
        F: for<'a> FnOnce(
958
0
            &'this <Y as Yokeable<'a>>::Output,
959
0
            PhantomData<&'a ()>,
960
0
        ) -> Result<<P as Yokeable<'a>>::Output, E>,
961
    {
962
0
        let p = f(self.get(), PhantomData)?;
963
0
        Ok(Yoke {
964
0
            yokeable: KindaSortaDangling::new(
965
0
                // Safety: the resulting `yokeable` is dropped before the `cart` because
966
0
                // of the Yoke invariant. See the safety docs below for the justification of why
967
0
                // yokeable could only borrow from the Cart.
968
0
                unsafe { P::make(p) },
969
0
            ),
970
0
            cart: self.cart.clone(),
971
0
        })
972
0
    }
973
    /// This is similar to [`Yoke::map_project`], but it works around older versions
974
    /// of Rust not being able to use `FnOnce` by using an explicit capture input.
975
    /// See [#1061](https://github.com/unicode-org/icu4x/issues/1061).
976
    ///
977
    /// See the docs of [`Yoke::map_project`] for how this works.
978
0
    pub fn map_project_with_explicit_capture<P, T>(
979
0
        self,
980
0
        capture: T,
981
0
        f: for<'a> fn(
982
0
            <Y as Yokeable<'a>>::Output,
983
0
            capture: T,
984
0
            PhantomData<&'a ()>,
985
0
        ) -> <P as Yokeable<'a>>::Output,
986
0
    ) -> Yoke<P, C>
987
0
    where
988
0
        P: for<'a> Yokeable<'a>,
989
    {
990
0
        let p = f(
991
0
            self.yokeable.into_inner().transform_owned(),
992
0
            capture,
993
0
            PhantomData,
994
0
        );
995
0
        Yoke {
996
0
            yokeable: KindaSortaDangling::new(
997
0
                // Safety: the resulting `yokeable` is dropped before the `cart` because
998
0
                // of the Yoke invariant. See the safety docs below for the justification of why
999
0
                // yokeable could only borrow from the Cart.
1000
0
                unsafe { P::make(p) },
1001
0
            ),
1002
0
            cart: self.cart,
1003
0
        }
1004
0
    }
1005
1006
    /// This is similar to [`Yoke::map_project_cloned`], but it works around older versions
1007
    /// of Rust not being able to use `FnOnce` by using an explicit capture input.
1008
    /// See [#1061](https://github.com/unicode-org/icu4x/issues/1061).
1009
    ///
1010
    /// See the docs of [`Yoke::map_project_cloned`] for how this works.
1011
0
    pub fn map_project_cloned_with_explicit_capture<'this, P, T>(
1012
0
        &'this self,
1013
0
        capture: T,
1014
0
        f: for<'a> fn(
1015
0
            &'this <Y as Yokeable<'a>>::Output,
1016
0
            capture: T,
1017
0
            PhantomData<&'a ()>,
1018
0
        ) -> <P as Yokeable<'a>>::Output,
1019
0
    ) -> Yoke<P, C>
1020
0
    where
1021
0
        P: for<'a> Yokeable<'a>,
1022
0
        C: CloneableCart,
1023
    {
1024
0
        let p = f(self.get(), capture, PhantomData);
1025
0
        Yoke {
1026
0
            yokeable: KindaSortaDangling::new(
1027
0
                // Safety: the resulting `yokeable` is dropped before the `cart` because
1028
0
                // of the Yoke invariant. See the safety docs below for the justification of why
1029
0
                // yokeable could only borrow from the Cart.
1030
0
                unsafe { P::make(p) },
1031
0
            ),
1032
0
            cart: self.cart.clone(),
1033
0
        }
1034
0
    }
1035
1036
    /// This is similar to [`Yoke::try_map_project`], but it works around older versions
1037
    /// of Rust not being able to use `FnOnce` by using an explicit capture input.
1038
    /// See [#1061](https://github.com/unicode-org/icu4x/issues/1061).
1039
    ///
1040
    /// See the docs of [`Yoke::try_map_project`] for how this works.
1041
    #[allow(clippy::type_complexity)]
1042
0
    pub fn try_map_project_with_explicit_capture<P, T, E>(
1043
0
        self,
1044
0
        capture: T,
1045
0
        f: for<'a> fn(
1046
0
            <Y as Yokeable<'a>>::Output,
1047
0
            capture: T,
1048
0
            PhantomData<&'a ()>,
1049
0
        ) -> Result<<P as Yokeable<'a>>::Output, E>,
1050
0
    ) -> Result<Yoke<P, C>, E>
1051
0
    where
1052
0
        P: for<'a> Yokeable<'a>,
1053
    {
1054
0
        let p = f(
1055
0
            self.yokeable.into_inner().transform_owned(),
1056
0
            capture,
1057
0
            PhantomData,
1058
0
        )?;
1059
0
        Ok(Yoke {
1060
0
            yokeable: KindaSortaDangling::new(
1061
0
                // Safety: the resulting `yokeable` is dropped before the `cart` because
1062
0
                // of the Yoke invariant. See the safety docs below for the justification of why
1063
0
                // yokeable could only borrow from the Cart.
1064
0
                unsafe { P::make(p) },
1065
0
            ),
1066
0
            cart: self.cart,
1067
0
        })
1068
0
    }
1069
1070
    /// This is similar to [`Yoke::try_map_project_cloned`], but it works around older versions
1071
    /// of Rust not being able to use `FnOnce` by using an explicit capture input.
1072
    /// See [#1061](https://github.com/unicode-org/icu4x/issues/1061).
1073
    ///
1074
    /// See the docs of [`Yoke::try_map_project_cloned`] for how this works.
1075
    #[allow(clippy::type_complexity)]
1076
0
    pub fn try_map_project_cloned_with_explicit_capture<'this, P, T, E>(
1077
0
        &'this self,
1078
0
        capture: T,
1079
0
        f: for<'a> fn(
1080
0
            &'this <Y as Yokeable<'a>>::Output,
1081
0
            capture: T,
1082
0
            PhantomData<&'a ()>,
1083
0
        ) -> Result<<P as Yokeable<'a>>::Output, E>,
1084
0
    ) -> Result<Yoke<P, C>, E>
1085
0
    where
1086
0
        P: for<'a> Yokeable<'a>,
1087
0
        C: CloneableCart,
1088
    {
1089
0
        let p = f(self.get(), capture, PhantomData)?;
1090
0
        Ok(Yoke {
1091
0
            yokeable: KindaSortaDangling::new(
1092
0
                // Safety: the resulting `yokeable` is dropped before the `cart` because
1093
0
                // of the Yoke invariant. See the safety docs below for the justification of why
1094
0
                // yokeable could only borrow from the Cart.
1095
0
                unsafe { P::make(p) },
1096
0
            ),
1097
0
            cart: self.cart.clone(),
1098
0
        })
1099
0
    }
1100
}
1101
1102
#[cfg(feature = "alloc")]
1103
impl<Y: for<'a> Yokeable<'a>, C: 'static + Sized> Yoke<Y, Rc<C>> {
1104
    /// Allows type-erasing the cart in a `Yoke<Y, Rc<C>>`.
1105
    ///
1106
    /// The yoke only carries around a cart type `C` for its destructor,
1107
    /// since it needs to be able to guarantee that its internal references
1108
    /// are valid for the lifetime of the Yoke. As such, the actual type of the
1109
    /// Cart is not very useful unless you wish to extract data out of it
1110
    /// via [`Yoke::backing_cart()`]. Erasing the cart allows for one to mix
1111
    /// [`Yoke`]s obtained from different sources.
1112
    ///
1113
    /// In case the cart type `C` is not already an `Rc<T>`, you can use
1114
    /// [`Yoke::wrap_cart_in_rc()`] to wrap it.
1115
    ///
1116
    /// ✨ *Enabled with the `alloc` Cargo feature.*
1117
    ///
1118
    /// # Example
1119
    ///
1120
    /// ```rust
1121
    /// use std::rc::Rc;
1122
    /// use yoke::erased::ErasedRcCart;
1123
    /// use yoke::Yoke;
1124
    ///
1125
    /// let buffer1: Rc<String> = Rc::new("   foo bar baz  ".into());
1126
    /// let buffer2: Box<String> = Box::new("  baz quux  ".into());
1127
    ///
1128
    /// let yoke1 =
1129
    ///     Yoke::<&'static str, _>::attach_to_cart(buffer1, |rc| rc.trim());
1130
    /// let yoke2 = Yoke::<&'static str, _>::attach_to_cart(buffer2, |b| b.trim());
1131
    ///
1132
    /// let erased1: Yoke<_, ErasedRcCart> = yoke1.erase_rc_cart();
1133
    /// // Wrap the Box in an Rc to make it compatible
1134
    /// let erased2: Yoke<_, ErasedRcCart> =
1135
    ///     yoke2.wrap_cart_in_rc().erase_rc_cart();
1136
    ///
1137
    /// // Now erased1 and erased2 have the same type!
1138
    /// ```
1139
0
    pub fn erase_rc_cart(self) -> Yoke<Y, ErasedRcCart> {
1140
        // Safety: safe because the cart is preserved, as it is just type-erased
1141
0
        unsafe { self.replace_cart(|c| c as ErasedRcCart) }
1142
0
    }
1143
}
1144
1145
#[cfg(feature = "alloc")]
1146
impl<Y: for<'a> Yokeable<'a>, C: 'static + Sized + Send + Sync> Yoke<Y, Arc<C>> {
1147
    /// Allows type-erasing the cart in a `Yoke<Y, Arc<C>>`.
1148
    ///
1149
    /// The yoke only carries around a cart type `C` for its destructor,
1150
    /// since it needs to be able to guarantee that its internal references
1151
    /// are valid for the lifetime of the Yoke. As such, the actual type of the
1152
    /// Cart is not very useful unless you wish to extract data out of it
1153
    /// via [`Yoke::backing_cart()`]. Erasing the cart allows for one to mix
1154
    /// [`Yoke`]s obtained from different sources.
1155
    ///
1156
    /// In case the cart type `C` is not already an `Arc<T>`, you can use
1157
    /// [`Yoke::wrap_cart_in_arc()`] to wrap it.
1158
    ///
1159
    /// ✨ *Enabled with the `alloc` Cargo feature.*
1160
    ///
1161
    /// # Example
1162
    ///
1163
    /// ```rust
1164
    /// use std::sync::Arc;
1165
    /// use yoke::erased::ErasedArcCart;
1166
    /// use yoke::Yoke;
1167
    ///
1168
    /// let buffer1: Arc<String> = Arc::new("   foo bar baz  ".into());
1169
    /// let buffer2: Box<String> = Box::new("  baz quux  ".into());
1170
    ///
1171
    /// let yoke1 =
1172
    ///     Yoke::<&'static str, _>::attach_to_cart(buffer1, |arc| arc.trim());
1173
    /// let yoke2 = Yoke::<&'static str, _>::attach_to_cart(buffer2, |b| b.trim());
1174
    ///
1175
    /// let erased1: Yoke<_, ErasedArcCart> = yoke1.erase_arc_cart();
1176
    /// // Wrap the Box in an Rc to make it compatible
1177
    /// let erased2: Yoke<_, ErasedArcCart> =
1178
    ///     yoke2.wrap_cart_in_arc().erase_arc_cart();
1179
    ///
1180
    /// // Now erased1 and erased2 have the same type!
1181
    /// ```
1182
0
    pub fn erase_arc_cart(self) -> Yoke<Y, ErasedArcCart> {
1183
        // Safety: safe because the cart is preserved, as it is just type-erased
1184
0
        unsafe { self.replace_cart(|c| c as ErasedArcCart) }
1185
0
    }
1186
}
1187
1188
#[cfg(feature = "alloc")]
1189
impl<Y: for<'a> Yokeable<'a>, C: 'static + Sized> Yoke<Y, Box<C>> {
1190
    /// Allows type-erasing the cart in a `Yoke<Y, Box<C>>`.
1191
    ///
1192
    /// The yoke only carries around a cart type `C` for its destructor,
1193
    /// since it needs to be able to guarantee that its internal references
1194
    /// are valid for the lifetime of the Yoke. As such, the actual type of the
1195
    /// Cart is not very useful unless you wish to extract data out of it
1196
    /// via [`Yoke::backing_cart()`]. Erasing the cart allows for one to mix
1197
    /// [`Yoke`]s obtained from different sources.
1198
    ///
1199
    /// In case the cart type `C` is not already `Box<T>`, you can use
1200
    /// [`Yoke::wrap_cart_in_box()`] to wrap it.
1201
    ///
1202
    /// ✨ *Enabled with the `alloc` Cargo feature.*
1203
    ///
1204
    /// # Example
1205
    ///
1206
    /// ```rust
1207
    /// use std::rc::Rc;
1208
    /// use yoke::erased::ErasedBoxCart;
1209
    /// use yoke::Yoke;
1210
    ///
1211
    /// let buffer1: Rc<String> = Rc::new("   foo bar baz  ".into());
1212
    /// let buffer2: Box<String> = Box::new("  baz quux  ".into());
1213
    ///
1214
    /// let yoke1 =
1215
    ///     Yoke::<&'static str, _>::attach_to_cart(buffer1, |rc| rc.trim());
1216
    /// let yoke2 = Yoke::<&'static str, _>::attach_to_cart(buffer2, |b| b.trim());
1217
    ///
1218
    /// // Wrap the Rc in an Box to make it compatible
1219
    /// let erased1: Yoke<_, ErasedBoxCart> =
1220
    ///     yoke1.wrap_cart_in_box().erase_box_cart();
1221
    /// let erased2: Yoke<_, ErasedBoxCart> = yoke2.erase_box_cart();
1222
    ///
1223
    /// // Now erased1 and erased2 have the same type!
1224
    /// ```
1225
0
    pub fn erase_box_cart(self) -> Yoke<Y, ErasedBoxCart> {
1226
        // Safety: safe because the cart is preserved, as it is just type-erased
1227
0
        unsafe { self.replace_cart(|c| c as ErasedBoxCart) }
1228
0
    }
1229
}
1230
1231
#[cfg(feature = "alloc")]
1232
impl<Y: for<'a> Yokeable<'a>, C> Yoke<Y, C> {
1233
    /// Helper function allowing one to wrap the cart type `C` in a `Box<T>`.
1234
    /// Can be paired with [`Yoke::erase_box_cart()`]
1235
    ///
1236
    /// ✨ *Enabled with the `alloc` Cargo feature.*
1237
    #[inline]
1238
0
    pub fn wrap_cart_in_box(self) -> Yoke<Y, Box<C>> {
1239
        // Safety: safe because the cart is preserved, as it is just wrapped.
1240
0
        unsafe { self.replace_cart(Box::new) }
1241
0
    }
1242
    /// Helper function allowing one to wrap the cart type `C` in an `Rc<T>`.
1243
    /// Can be paired with [`Yoke::erase_rc_cart()`], or generally used
1244
    /// to make the [`Yoke`] cloneable.
1245
    ///
1246
    /// ✨ *Enabled with the `alloc` Cargo feature.*
1247
    #[inline]
1248
0
    pub fn wrap_cart_in_rc(self) -> Yoke<Y, Rc<C>> {
1249
        // Safety: safe because the cart is preserved, as it is just wrapped
1250
0
        unsafe { self.replace_cart(Rc::new) }
1251
0
    }
1252
    /// Helper function allowing one to wrap the cart type `C` in an `Rc<T>`.
1253
    /// Can be paired with [`Yoke::erase_arc_cart()`], or generally used
1254
    /// to make the [`Yoke`] cloneable.
1255
    ///
1256
    /// ✨ *Enabled with the `alloc` Cargo feature.*
1257
    #[inline]
1258
0
    pub fn wrap_cart_in_arc(self) -> Yoke<Y, Arc<C>> {
1259
        // Safety: safe because the cart is preserved, as it is just wrapped
1260
0
        unsafe { self.replace_cart(Arc::new) }
1261
0
    }
1262
}
1263
1264
impl<Y: for<'a> Yokeable<'a>, C> Yoke<Y, C> {
1265
    /// Helper function allowing one to wrap the cart type `C` in an [`EitherCart`].
1266
    ///
1267
    /// This function wraps the cart into the `A` variant. To wrap it into the
1268
    /// `B` variant, use [`Self::wrap_cart_in_either_b()`].
1269
    ///
1270
    /// For an example, see [`EitherCart`].
1271
    #[inline]
1272
0
    pub fn wrap_cart_in_either_a<B>(self) -> Yoke<Y, EitherCart<C, B>> {
1273
        // Safety: safe because the cart is preserved, as it is just wrapped.
1274
0
        unsafe { self.replace_cart(EitherCart::A) }
1275
0
    }
1276
    /// Helper function allowing one to wrap the cart type `C` in an [`EitherCart`].
1277
    ///
1278
    /// This function wraps the cart into the `B` variant. To wrap it into the
1279
    /// `A` variant, use [`Self::wrap_cart_in_either_a()`].
1280
    ///
1281
    /// For an example, see [`EitherCart`].
1282
    #[inline]
1283
0
    pub fn wrap_cart_in_either_b<A>(self) -> Yoke<Y, EitherCart<A, C>> {
1284
        // Safety: safe because the cart is preserved, as it is just wrapped.
1285
0
        unsafe { self.replace_cart(EitherCart::B) }
1286
0
    }
1287
}
1288
1289
/// # Safety docs for project()
1290
///
1291
/// (Docs are on a private const to allow the use of compile_fail doctests)
1292
///
1293
/// This is safe to perform because of the choice of lifetimes on `f`, that is,
1294
/// `for<a> fn(<Y as Yokeable<'a>>::Output, &'a ()) -> <P as Yokeable<'a>>::Output`.
1295
///
1296
/// Note that correctness arguments are similar if you replace `fn` with `FnOnce`.
1297
///
1298
/// What we want this function to do is take a Yokeable (`Y`) that is borrowing from the cart, and
1299
/// produce another Yokeable (`P`) that also borrows from the same cart. There are a couple potential
1300
/// hazards here:
1301
///
1302
/// - `P` ends up borrowing data from `Y` (or elsewhere) that did _not_ come from the cart,
1303
///   for example `P` could borrow owned data from a `Cow`. This would make the `Yoke<P>` dependent
1304
///   on data owned only by the `Yoke<Y>`.
1305
/// - Borrowed data from `Y` escapes with the wrong lifetime
1306
///
1307
/// Let's walk through these and see how they're prevented.
1308
///
1309
/// ```rust, compile_fail
1310
/// # use std::rc::Rc;
1311
/// # use yoke::Yoke;
1312
/// # use std::borrow::Cow;
1313
/// fn borrow_potentially_owned(y: &Yoke<Cow<'static, str>, Rc<[u8]>>) -> Yoke<&'static str, Rc<[u8]>> {
1314
///    y.map_project_cloned(|cow, _| &*cow)   
1315
/// }
1316
/// ```
1317
///
1318
/// In this case, the lifetime of `&*cow` is `&'this str`, however the function needs to be able to return
1319
/// `&'a str` _for all `'a`_, which isn't possible.
1320
///
1321
///
1322
/// ```rust, compile_fail
1323
/// # use std::rc::Rc;
1324
/// # use yoke::Yoke;
1325
/// # use std::borrow::Cow;
1326
/// fn borrow_potentially_owned(y: Yoke<Cow<'static, str>, Rc<[u8]>>) -> Yoke<&'static str, Rc<[u8]>> {
1327
///    y.map_project(|cow, _| &*cow)   
1328
/// }
1329
/// ```
1330
///
1331
/// This has the same issue, `&*cow` is borrowing for a local lifetime.
1332
///
1333
/// Similarly, trying to project an owned field of a struct will produce similar errors:
1334
///
1335
/// ```rust,compile_fail
1336
/// # use std::borrow::Cow;
1337
/// # use yoke::{Yoke, Yokeable};
1338
/// # use std::mem;
1339
/// # use std::rc::Rc;
1340
/// #
1341
/// // also safely implements Yokeable<'a>
1342
/// struct Bar<'a> {
1343
///     owned: String,
1344
///     string_2: &'a str,
1345
/// }
1346
///
1347
/// fn map_project_owned(bar: &Yoke<Bar<'static>, Rc<[u8]>>) -> Yoke<&'static str, Rc<[u8]>> {
1348
///     // ERROR (but works if you replace owned with string_2)
1349
///     bar.map_project_cloned(|bar, _| &*bar.owned)   
1350
/// }
1351
///
1352
/// #
1353
/// # unsafe impl<'a> Yokeable<'a> for Bar<'static> {
1354
/// #     type Output = Bar<'a>;
1355
/// #     fn transform(&'a self) -> &'a Bar<'a> {
1356
/// #         self
1357
/// #     }
1358
/// #
1359
/// #     fn transform_owned(self) -> Bar<'a> {
1360
/// #         // covariant lifetime cast, can be done safely
1361
/// #         self
1362
/// #     }
1363
/// #
1364
/// #     unsafe fn make(from: Bar<'a>) -> Self {
1365
/// #         let ret = mem::transmute_copy(&from);
1366
/// #         mem::forget(from);
1367
/// #         ret
1368
/// #     }
1369
/// #
1370
/// #     fn transform_mut<F>(&'a mut self, f: F)
1371
/// #     where
1372
/// #         F: 'static + FnOnce(&'a mut Self::Output),
1373
/// #     {
1374
/// #         unsafe { f(mem::transmute(self)) }
1375
/// #     }
1376
/// # }
1377
/// ```
1378
///
1379
/// Borrowed data from `Y` similarly cannot escape with the wrong lifetime because of the `for<'a>`, since
1380
/// it will never be valid for the borrowed data to escape for all lifetimes of 'a. Internally, `.project()`
1381
/// uses `.get()`, however the signature forces the callers to be able to handle every lifetime.
1382
///
1383
///  `'a` is the only lifetime that matters here; `Yokeable`s must be `'static` and since
1384
/// `Output` is an associated type it can only have one lifetime, `'a` (there's nowhere for it to get another from).
1385
/// `Yoke`s can get additional lifetimes via the cart, and indeed, `project()` can operate on `Yoke<_, &'b [u8]>`,
1386
/// however this lifetime is inaccessible to the closure, and even if it were accessible the `for<'a>` would force
1387
/// it out of the output. All external lifetimes (from other found outside the yoke/closures
1388
/// are similarly constrained here.
1389
///
1390
/// Essentially, safety is achieved by using `for<'a> fn(...)` with `'a` used in both `Yokeable`s to ensure that
1391
/// the output yokeable can _only_ have borrowed data flow in to it from the input. All paths of unsoundness require the
1392
/// unification of an existential and universal lifetime, which isn't possible.
1393
const _: () = ();
1394
1395
/// # Safety docs for attach_to_cart()'s signature
1396
///
1397
/// The `attach_to_cart()` family of methods get by by using the following bound:
1398
///
1399
/// ```rust,ignore
1400
/// F: for<'de> FnOnce(&'de <C as Deref>::Target) -> <Y as Yokeable<'de>>::Output,
1401
/// C::Target: 'static
1402
/// ```
1403
///
1404
/// to enforce that the yoking closure produces a yokeable that is *only* allowed to borrow from the cart.
1405
/// A way to be sure of this is as follows: imagine if `F` *did* borrow data of lifetime `'a` and stuff it in
1406
/// its output. Then that lifetime `'a` would have to live at least as long as `'de` *for all `'de`*.
1407
/// The only lifetime that satisfies that is `'static` (since at least one of the potential `'de`s is `'static`),
1408
/// and we're fine with that.
1409
///
1410
/// ## Implied bounds and variance
1411
///
1412
/// The `C::Target: 'static` bound is tricky, however. Let's imagine a situation where we *didn't* have that bound.
1413
///
1414
/// One thing to remember is that we are okay with the cart itself borrowing from places,
1415
/// e.g. `&[u8]` is a valid cart, as is `Box<&[u8]>`. `C` is not `'static`.
1416
///
1417
/// (I'm going to use `CT` in prose to refer to `C::Target` here, since almost everything here has to do
1418
/// with C::Target and not C itself.)
1419
///
1420
/// Unfortunately, there's a sneaky additional bound inside `F`. The signature of `F` is *actually*
1421
///
1422
/// ```rust,ignore
1423
/// F: for<'de> where<C::Target: 'de> FnOnce(&'de C::Target) -> <Y as Yokeable<'de>>::Output
1424
/// ```
1425
///
1426
/// using made-up "where clause inside HRTB" syntax to represent a type that can be represented inside the compiler
1427
/// and type system but not in Rust code. The `CT: 'de` bond comes from the `&'de C::Target`: any time you
1428
/// write `&'a T`, an implied bound of `T: 'a` materializes and is stored alongside it, since references cannot refer
1429
/// to data that itself refers to data of shorter lifetimes. If a reference is valid, its referent must be valid for
1430
/// the duration of the reference's lifetime, so every reference *inside* its referent must also be valid, giving us `T: 'a`.
1431
/// This kind of constraint is often called a "well formedness" constraint: `&'a T` is not "well formed" without that
1432
/// bound, and rustc is being helpful by giving it to us for free.
1433
///
1434
/// Unfortunately, this messes with our universal quantification. The `for<'de>` is no longer "For all lifetimes `'de`",
1435
/// it is "for all lifetimes `'de` *where `CT: 'de`*". And if `CT` borrows from somewhere (with lifetime `'ct`), then we get a
1436
/// `'ct: 'de` bound, and `'de` candidates that live longer than `'ct` won't actually be considered.
1437
/// The neat little logic at the beginning stops working.
1438
///
1439
/// `attach_to_cart()` will instead enforce that the produced yokeable *either* borrows from the cart (fine), or from
1440
/// data that has a lifetime that is at least `'ct`. Which means that `attach_to_cart()` will allow us to borrow locals
1441
/// provided they live at least as long as `'ct`.
1442
///
1443
/// Is this a problem?
1444
///
1445
/// This is totally fine if CT's lifetime is covariant: if C is something like `Box<&'ct [u8]>`, even if our
1446
/// yoked object borrows from locals outliving `'ct`, our Yoke can't outlive that
1447
/// lifetime `'ct` anyway (since it's a part of the cart type), so we're fine.
1448
///
1449
/// However it's completely broken for contravariant carts (e.g. `Box<fn(&'ct u8)>`). In that case
1450
/// we still get `'ct: 'de`, and we still end up being able to
1451
/// borrow from locals that outlive `'ct`. However, our Yoke _can_ outlive
1452
/// that lifetime, because Yoke shares its variance over `'ct`
1453
/// with the cart type, and the cart type is contravariant over `'ct`.
1454
/// So the Yoke can be upcast to having a longer lifetime than `'ct`, and *that* Yoke
1455
/// can outlive `'ct`.
1456
///
1457
/// We fix this by forcing `C::Target: 'static` in `attach_to_cart()`, which would make it work
1458
/// for fewer types, but would also allow Yoke to continue to be covariant over cart lifetimes if necessary.
1459
///
1460
/// An alternate fix would be to not allowing yoke to ever be upcast over lifetimes contained in the cart
1461
/// by forcing them to be invariant. This is a bit more restrictive and affects *all* `Yoke` users, not just
1462
/// those using `attach_to_cart()`.
1463
///
1464
/// See https://github.com/unicode-org/icu4x/issues/2926
1465
/// See also https://github.com/rust-lang/rust/issues/106431 for potentially fixing this upstream by
1466
/// changing how the bound works.
1467
///
1468
/// # Tests
1469
///
1470
/// Here's a broken `attach_to_cart()` that attempts to borrow from a local:
1471
///
1472
/// ```rust,compile_fail
1473
/// use yoke::Yoke;
1474
///
1475
/// let cart = vec![1, 2, 3, 4].into_boxed_slice();
1476
/// let local = vec![4, 5, 6, 7];
1477
/// let yoke: Yoke<&[u8], Box<[u8]>> = Yoke::attach_to_cart(cart, |_| &*local);
1478
/// ```
1479
///
1480
/// Fails as expected.
1481
///
1482
/// And here's a working one with a local borrowed cart that does not do any sneaky borrows whilst attaching.
1483
///
1484
/// ```rust
1485
/// use yoke::Yoke;
1486
///
1487
/// let cart = vec![1, 2, 3, 4].into_boxed_slice();
1488
/// let local = vec![4, 5, 6, 7];
1489
/// let yoke: Yoke<&[u8], &[u8]> = Yoke::attach_to_cart(&cart, |c| &*c);
1490
/// ```
1491
///
1492
/// Here's an `attach_to_cart()` that attempts to borrow from a longer-lived local due to
1493
/// the cart being covariant. It fails, but would not if the alternate fix of forcing Yoke to be invariant
1494
/// were implemented. It is technically a safe operation:
1495
///
1496
/// ```rust,compile_fail
1497
/// use yoke::Yoke;
1498
/// // longer lived
1499
/// let local = vec![4, 5, 6, 7];
1500
///
1501
/// let backing = vec![1, 2, 3, 4];
1502
/// let cart = Box::new(&*backing);
1503
///
1504
/// let yoke: Yoke<&[u8], Box<&[u8]>> = Yoke::attach_to_cart(cart, |_| &*local);
1505
/// println!("{:?}", yoke.get());
1506
/// ```
1507
///
1508
/// Finally, here's an `attach_to_cart()` that attempts to borrow from a longer lived local
1509
/// in the case of a contravariant lifetime. It does not compile, but in and of itself is not dangerous:
1510
///
1511
/// ```rust,compile_fail
1512
/// use yoke::Yoke;
1513
///
1514
/// type Contra<'a> = fn(&'a ());
1515
///
1516
/// let local = String::from("Hello World!");
1517
/// let yoke: Yoke<&'static str, Box<Contra<'_>>> = Yoke::attach_to_cart(Box::new((|_| {}) as _), |_| &local[..]);
1518
/// println!("{:?}", yoke.get());
1519
/// ```
1520
///
1521
/// It is dangerous if allowed to transform (testcase from #2926)
1522
///
1523
/// ```rust,compile_fail
1524
/// use yoke::Yoke;
1525
///
1526
/// type Contra<'a> = fn(&'a ());
1527
///
1528
///
1529
/// let local = String::from("Hello World!");
1530
/// let yoke: Yoke<&'static str, Box<Contra<'_>>> = Yoke::attach_to_cart(Box::new((|_| {}) as _), |_| &local[..]);
1531
/// println!("{:?}", yoke.get());
1532
/// let yoke_longer: Yoke<&'static str, Box<Contra<'static>>> = yoke;
1533
/// let leaked: &'static Yoke<&'static str, Box<Contra<'static>>> = Box::leak(Box::new(yoke_longer));
1534
/// let reference: &'static str = leaked.get();
1535
///
1536
/// println!("pre-drop: {reference}");
1537
/// drop(local);
1538
/// println!("post-drop: {reference}");
1539
/// ```
1540
const _: () = ();