Coverage Report

Created: 2026-03-21 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/yoke-0.8.1/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::utils;
11
use crate::Yokeable;
12
use core::marker::PhantomData;
13
use core::ops::Deref;
14
use stable_deref_trait::StableDeref;
15
16
#[cfg(feature = "alloc")]
17
use alloc::boxed::Box;
18
#[cfg(feature = "alloc")]
19
use alloc::rc::Rc;
20
#[cfg(feature = "alloc")]
21
use alloc::sync::Arc;
22
23
/// A Cow-like borrowed object "yoked" to its backing data.
24
///
25
/// This allows things like zero copy deserialized data to carry around
26
/// shared references to their backing buffer, by "erasing" their static lifetime
27
/// and turning it into a dynamically managed one.
28
///
29
/// `Y` (the [`Yokeable`]) is the object containing the references,
30
/// and will typically be of the form `Foo<'static>`. The `'static` is
31
/// not the actual lifetime of the data, rather it is a convenient way to mark the
32
/// erased lifetime and make it dynamic.
33
///
34
/// `C` is the "cart", which `Y` may contain references to. After the yoke is constructed,
35
/// the cart serves little purpose except to guarantee that `Y`'s references remain valid
36
/// for as long as the yoke remains in memory (by calling the destructor at the appropriate moment).
37
///
38
/// The primary constructor for [`Yoke`] is [`Yoke::attach_to_cart()`]. Several variants of that
39
/// constructor are provided to serve numerous types of call sites and `Yoke` signatures.
40
///
41
/// The key behind this type is [`Yoke::get()`], where calling [`.get()`][Yoke::get] on a type like
42
/// `Yoke<Cow<'static, str>, _>` will get you a short-lived `&'a Cow<'a, str>`, restricted to the
43
/// lifetime of the borrow used during `.get()`. This is entirely safe since the `Cow` borrows from
44
/// the cart type `C`, which cannot be interfered with as long as the `Yoke` is borrowed by `.get
45
/// ()`. `.get()` protects access by essentially reifying the erased lifetime to a safe local one
46
/// when necessary.
47
///
48
/// Furthermore, there are various [`.map_project()`][Yoke::map_project] methods that allow turning a `Yoke`
49
/// into another `Yoke` containing a different type that may contain elements of the original yoked
50
/// value. See the [`Yoke::map_project()`] docs for more details.
51
///
52
/// In general, `C` is a concrete type, but it is also possible for it to be a trait object.
53
///
54
/// # Example
55
///
56
/// For example, we can use this to store zero-copy deserialized data in a cache:
57
///
58
/// ```rust
59
/// # use yoke::Yoke;
60
/// # use std::rc::Rc;
61
/// # use std::borrow::Cow;
62
/// # fn load_from_cache(_filename: &str) -> Rc<[u8]> {
63
/// #     // dummy implementation
64
/// #     Rc::new([0x5, 0, 0, 0, 0, 0, 0, 0, 0x68, 0x65, 0x6c, 0x6c, 0x6f])
65
/// # }
66
///
67
/// fn load_object(filename: &str) -> Yoke<Cow<'static, str>, Rc<[u8]>> {
68
///     let rc: Rc<[u8]> = load_from_cache(filename);
69
///     Yoke::<Cow<'static, str>, Rc<[u8]>>::attach_to_cart(rc, |data: &[u8]| {
70
///         // essentially forcing a #[serde(borrow)]
71
///         Cow::Borrowed(bincode::deserialize(data).unwrap())
72
///     })
73
/// }
74
///
75
/// let yoke = load_object("filename.bincode");
76
/// assert_eq!(&**yoke.get(), "hello");
77
/// assert!(matches!(yoke.get(), &Cow::Borrowed(_)));
78
/// ```
79
pub struct Yoke<Y: for<'a> Yokeable<'a>, C> {
80
    // must be the first field for drop order
81
    // this will have a 'static lifetime parameter, that parameter is a lie
82
    yokeable: KindaSortaDangling<Y>,
83
    // Safety invariant: this type can be anything, but `yokeable` may only contain references to
84
    // StableDeref parts of this cart, and the targets of those references must be valid for the
85
    // lifetime of this cart (it must own or borrow them). It's ok for this cart to contain stack
86
    // data as long as it is not referenced by `yokeable` during construction. `attach_to_cart`,
87
    // the typical constructor of this type, upholds this invariant, but other constructors like
88
    // `replace_cart` need to uphold it.
89
    // The implementation guarantees that there are no live `yokeable`s that reference data
90
    // in a `cart` when the `cart` is dropped; this is guaranteed in the drop glue through field
91
    // order.
92
    cart: C,
93
}
94
95
// Manual `Debug` implementation, since the derived one would be unsound.
96
// See https://github.com/unicode-org/icu4x/issues/3685
97
impl<Y: for<'a> Yokeable<'a>, C: core::fmt::Debug> core::fmt::Debug for Yoke<Y, C>
98
where
99
    for<'a> <Y as Yokeable<'a>>::Output: core::fmt::Debug,
100
{
101
0
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
102
0
        f.debug_struct("Yoke")
103
0
            .field("yokeable", self.get())
104
0
            .field("cart", self.backing_cart())
105
0
            .finish()
106
0
    }
107
}
108
109
#[test]
110
fn test_debug() {
111
    let local_data = "foo".to_owned();
112
    let y1 = Yoke::<alloc::borrow::Cow<'static, str>, Rc<String>>::attach_to_zero_copy_cart(
113
        Rc::new(local_data),
114
    );
115
    assert_eq!(
116
        format!("{y1:?}"),
117
        r#"Yoke { yokeable: "foo", cart: "foo" }"#,
118
    );
119
}
120
121
impl<Y: for<'a> Yokeable<'a>, C: StableDeref> Yoke<Y, C>
122
where
123
    <C as Deref>::Target: 'static,
124
{
125
    /// Construct a [`Yoke`] by yokeing an object to a cart in a closure.
126
    ///
127
    /// The closure can read and write data outside of its scope, but data it returns
128
    /// may borrow only from the argument passed to the closure.
129
    ///
130
    /// See also [`Yoke::try_attach_to_cart()`] to return a `Result` from the closure.
131
    ///
132
    /// Call sites for this function may not compile pre-1.61; if this still happens, use
133
    /// [`Yoke::attach_to_cart_badly()`] and file a bug.
134
    ///
135
    /// # Examples
136
    ///
137
    /// ```
138
    /// # use yoke::Yoke;
139
    /// # use std::rc::Rc;
140
    /// # use std::borrow::Cow;
141
    /// # fn load_from_cache(_filename: &str) -> Rc<[u8]> {
142
    /// #     // dummy implementation
143
    /// #     Rc::new([0x5, 0, 0, 0, 0, 0, 0, 0, 0x68, 0x65, 0x6c, 0x6c, 0x6f])
144
    /// # }
145
    ///
146
    /// fn load_object(filename: &str) -> Yoke<Cow<'static, str>, Rc<[u8]>> {
147
    ///     let rc: Rc<[u8]> = load_from_cache(filename);
148
    ///     Yoke::<Cow<'static, str>, Rc<[u8]>>::attach_to_cart(rc, |data: &[u8]| {
149
    ///         // essentially forcing a #[serde(borrow)]
150
    ///         Cow::Borrowed(bincode::deserialize(data).unwrap())
151
    ///     })
152
    /// }
153
    ///
154
    /// let yoke: Yoke<Cow<str>, _> = load_object("filename.bincode");
155
    /// assert_eq!(&**yoke.get(), "hello");
156
    /// assert!(matches!(yoke.get(), &Cow::Borrowed(_)));
157
    /// ```
158
    ///
159
    /// Write the number of consumed bytes to a local variable:
160
    ///
161
    /// ```
162
    /// # use yoke::Yoke;
163
    /// # use std::rc::Rc;
164
    /// # use std::borrow::Cow;
165
    /// # fn load_from_cache(_filename: &str) -> Rc<[u8]> {
166
    /// #     // dummy implementation
167
    /// #     Rc::new([0x5, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0, 0, 0])
168
    /// # }
169
    ///
170
    /// fn load_object(
171
    ///     filename: &str,
172
    /// ) -> (Yoke<Cow<'static, str>, Rc<[u8]>>, usize) {
173
    ///     let rc: Rc<[u8]> = load_from_cache(filename);
174
    ///     let mut bytes_remaining = 0;
175
    ///     let bytes_remaining = &mut bytes_remaining;
176
    ///     let yoke = Yoke::<Cow<'static, str>, Rc<[u8]>>::attach_to_cart(
177
    ///         rc,
178
    ///         |data: &[u8]| {
179
    ///             let mut d = postcard::Deserializer::from_bytes(data);
180
    ///             let output = serde::Deserialize::deserialize(&mut d);
181
    ///             *bytes_remaining = d.finalize().unwrap().len();
182
    ///             Cow::Borrowed(output.unwrap())
183
    ///         },
184
    ///     );
185
    ///     (yoke, *bytes_remaining)
186
    /// }
187
    ///
188
    /// let (yoke, bytes_remaining) = load_object("filename.postcard");
189
    /// assert_eq!(&**yoke.get(), "hello");
190
    /// assert!(matches!(yoke.get(), &Cow::Borrowed(_)));
191
    /// assert_eq!(bytes_remaining, 3);
192
    /// ```
193
0
    pub fn attach_to_cart<F>(cart: C, f: F) -> Self
194
0
    where
195
0
        // safety note: This works by enforcing that the *only* place the return value of F
196
0
        // can borrow from is the cart, since `F` must be valid for all lifetimes `'de`
197
0
        //
198
0
        // The <C as Deref>::Target: 'static on the impl is crucial for safety as well
199
0
        //
200
0
        // See safety docs at the bottom of this file for more information
201
0
        F: for<'de> FnOnce(&'de <C as Deref>::Target) -> <Y as Yokeable<'de>>::Output,
202
0
        <C as Deref>::Target: 'static,
203
    {
204
0
        let deserialized = f(cart.deref());
205
0
        Self {
206
0
            yokeable: KindaSortaDangling::new(
207
0
                // Safety: the resulting `yokeable` is dropped before the `cart` because
208
0
                // of the Yoke invariant. See the safety docs at the bottom of this file
209
0
                // for the justification of why yokeable could only borrow from the Cart.
210
0
                unsafe { Y::make(deserialized) },
211
0
            ),
212
0
            cart,
213
0
        }
214
0
    }
215
216
    /// Construct a [`Yoke`] by yokeing an object to a cart. If an error occurs in the
217
    /// deserializer function, the error is passed up to the caller.
218
    ///
219
    /// Call sites for this function may not compile pre-1.61; if this still happens, use
220
    /// [`Yoke::try_attach_to_cart_badly()`] and file a bug.
221
0
    pub fn try_attach_to_cart<E, F>(cart: C, f: F) -> Result<Self, E>
222
0
    where
223
0
        F: for<'de> FnOnce(&'de <C as Deref>::Target) -> Result<<Y as Yokeable<'de>>::Output, E>,
224
0
        <C as Deref>::Target: 'static,
225
    {
226
0
        let deserialized = f(cart.deref())?;
227
0
        Ok(Self {
228
0
            yokeable: KindaSortaDangling::new(
229
0
                // Safety: the resulting `yokeable` is dropped before the `cart` because
230
0
                // of the Yoke invariant. See the safety docs at the bottom of this file
231
0
                // for the justification of why yokeable could only borrow from the Cart.
232
0
                unsafe { Y::make(deserialized) },
233
0
            ),
234
0
            cart,
235
0
        })
236
0
    }
237
238
    /// Use [`Yoke::attach_to_cart()`].
239
    ///
240
    /// This was needed because the pre-1.61 compiler couldn't always handle the FnOnce trait bound.
241
    #[deprecated]
242
0
    pub fn attach_to_cart_badly(
243
0
        cart: C,
244
0
        f: for<'de> fn(&'de <C as Deref>::Target) -> <Y as Yokeable<'de>>::Output,
245
0
    ) -> Self {
246
0
        Self::attach_to_cart(cart, f)
247
0
    }
248
249
    /// Use [`Yoke::try_attach_to_cart()`].
250
    ///
251
    /// This was needed because the pre-1.61 compiler couldn't always handle the FnOnce trait bound.
252
    #[deprecated]
253
0
    pub fn try_attach_to_cart_badly<E>(
254
0
        cart: C,
255
0
        f: for<'de> fn(&'de <C as Deref>::Target) -> Result<<Y as Yokeable<'de>>::Output, E>,
256
0
    ) -> Result<Self, E> {
257
0
        Self::try_attach_to_cart(cart, f)
258
0
    }
259
}
260
261
impl<Y: for<'a> Yokeable<'a>, C> Yoke<Y, C> {
262
    /// Obtain a valid reference to the yokeable data
263
    ///
264
    /// This essentially transforms the lifetime of the internal yokeable data to
265
    /// be valid.
266
    /// For example, if you're working with a `Yoke<Cow<'static, T>, C>`, this
267
    /// will return an `&'a Cow<'a, T>`
268
    ///
269
    /// # Example
270
    ///
271
    /// ```rust
272
    /// # use yoke::Yoke;
273
    /// # use std::rc::Rc;
274
    /// # use std::borrow::Cow;
275
    /// # fn load_from_cache(_filename: &str) -> Rc<[u8]> {
276
    /// #     // dummy implementation
277
    /// #     Rc::new([0x5, 0, 0, 0, 0, 0, 0, 0, 0x68, 0x65, 0x6c, 0x6c, 0x6f])
278
    /// # }
279
    /// #
280
    /// # fn load_object(filename: &str) -> Yoke<Cow<'static, str>, Rc<[u8]>> {
281
    /// #     let rc: Rc<[u8]> = load_from_cache(filename);
282
    /// #     Yoke::<Cow<'static, str>, Rc<[u8]>>::attach_to_cart(rc, |data: &[u8]| {
283
    /// #         Cow::Borrowed(bincode::deserialize(data).unwrap())
284
    /// #     })
285
    /// # }
286
    ///
287
    /// // load_object() defined in the example at the top of this page
288
    /// let yoke: Yoke<Cow<str>, _> = load_object("filename.bincode");
289
    /// assert_eq!(yoke.get(), "hello");
290
    /// ```
291
    #[inline]
292
0
    pub fn get<'a>(&'a self) -> &'a <Y as Yokeable<'a>>::Output {
293
0
        self.yokeable.transform()
294
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
295
296
    /// Get a reference to the backing cart.
297
    ///
298
    /// This can be useful when building caches, etc. However, if you plan to store the cart
299
    /// separately from the yoke, read the note of caution below in [`Yoke::into_backing_cart`].
300
0
    pub fn backing_cart(&self) -> &C {
301
0
        &self.cart
302
0
    }
303
304
    /// Get the backing cart by value, dropping the yokeable object.
305
    ///
306
    /// **Caution:** Calling this method could cause information saved in the yokeable object but
307
    /// not the cart to be lost. Use this method only if the yokeable object cannot contain its
308
    /// own information.
309
    ///
310
    /// # Example
311
    ///
312
    /// Good example: the yokeable object is only a reference, so no information can be lost.
313
    ///
314
    /// ```
315
    /// use yoke::Yoke;
316
    ///
317
    /// let local_data = "foo".to_owned();
318
    /// let yoke = Yoke::<&'static str, Box<String>>::attach_to_zero_copy_cart(
319
    ///     Box::new(local_data),
320
    /// );
321
    /// assert_eq!(*yoke.get(), "foo");
322
    ///
323
    /// // Get back the cart
324
    /// let cart = yoke.into_backing_cart();
325
    /// assert_eq!(&*cart, "foo");
326
    /// ```
327
    ///
328
    /// Bad example: information specified in `.with_mut()` is lost.
329
    ///
330
    /// ```
331
    /// use std::borrow::Cow;
332
    /// use yoke::Yoke;
333
    ///
334
    /// let local_data = "foo".to_owned();
335
    /// let mut yoke =
336
    ///     Yoke::<Cow<'static, str>, Box<String>>::attach_to_zero_copy_cart(
337
    ///         Box::new(local_data),
338
    ///     );
339
    /// assert_eq!(yoke.get(), "foo");
340
    ///
341
    /// // Override data in the cart
342
    /// yoke.with_mut(|cow| {
343
    ///     let mut_str = cow.to_mut();
344
    ///     mut_str.clear();
345
    ///     mut_str.push_str("bar");
346
    /// });
347
    /// assert_eq!(yoke.get(), "bar");
348
    ///
349
    /// // Get back the cart
350
    /// let cart = yoke.into_backing_cart();
351
    /// assert_eq!(&*cart, "foo"); // WHOOPS!
352
    /// ```
353
0
    pub fn into_backing_cart(self) -> C {
354
0
        self.cart
355
0
    }
356
357
    /// Unsafe function for replacing the cart with another
358
    ///
359
    /// This can be used for type-erasing the cart, for example.
360
    ///
361
    /// # Safety
362
    ///
363
    /// - `f()` must not panic
364
    /// - References from the yokeable `Y` should still be valid for the lifetime of the
365
    ///   returned cart type `C`.
366
    ///
367
    ///   For the purpose of determining this, `Yoke` guarantees that references from the Yokeable
368
    ///   `Y` into the cart `C` will never be references into its stack data, only heap data protected
369
    ///   by `StableDeref`. This does not necessarily mean that `C` implements `StableDeref`, rather that
370
    ///   any data referenced by `Y` must be accessed through a `StableDeref` impl on something `C` owns.
371
    ///
372
    ///   Concretely, this means that if `C = Option<Rc<T>>`, `Y` may contain references to the `T` but not
373
    ///   anything else.
374
    /// - Lifetimes inside C must not be lengthened, even if they are themselves contravariant.
375
    ///   I.e., if C contains an `fn(&'a u8)`, it cannot be replaced with `fn(&'static u8),
376
    ///   even though that is typically safe.
377
    ///
378
    /// Typically, this means implementing `f` as something which _wraps_ the inner cart type `C`.
379
    /// `Yoke` only really cares about destructors for its carts so it's fine to erase other
380
    /// information about the cart, as long as the backing data will still be destroyed at the
381
    /// same time.
382
    #[inline]
383
0
    pub unsafe fn replace_cart<C2>(self, f: impl FnOnce(C) -> C2) -> Yoke<Y, C2> {
384
0
        Yoke {
385
0
            // Safety note: the safety invariant of this function guarantees that
386
0
            // the data that the yokeable references has its ownership (if any)
387
0
            // transferred to the new cart before self.cart is dropped.
388
0
            yokeable: self.yokeable,
389
0
            cart: f(self.cart),
390
0
        }
391
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::<_, _>
392
393
    /// Mutate the stored [`Yokeable`] data.
394
    ///
395
    /// If the callback needs to return `'static` data, then [`Yoke::with_mut_return`] can be
396
    /// used until the next breaking release of `yoke`, at which time the callback to this
397
    /// function will be able to return any `'static` data.
398
    ///
399
    /// See [`Yokeable::transform_mut()`] for why this operation is safe.
400
    ///
401
    /// # Example
402
    ///
403
    /// This can be used to partially mutate the stored data, provided
404
    /// no _new_ borrowed data is introduced.
405
    ///
406
    /// ```rust
407
    /// # use yoke::{Yoke, Yokeable};
408
    /// # use std::rc::Rc;
409
    /// # use std::borrow::Cow;
410
    /// # use std::mem;
411
    /// # fn load_from_cache(_filename: &str) -> Rc<[u8]> {
412
    /// #     // dummy implementation
413
    /// #     Rc::new([0x5, 0, 0, 0, 0, 0, 0, 0, 0x68, 0x65, 0x6c, 0x6c, 0x6f])
414
    /// # }
415
    /// #
416
    /// # fn load_object(filename: &str) -> Yoke<Bar<'static>, Rc<[u8]>> {
417
    /// #     let rc: Rc<[u8]> = load_from_cache(filename);
418
    /// #     Yoke::<Bar<'static>, Rc<[u8]>>::attach_to_cart(rc, |data: &[u8]| {
419
    /// #         // A real implementation would properly deserialize `Bar` as a whole
420
    /// #         Bar {
421
    /// #             numbers: Cow::Borrowed(bincode::deserialize(data).unwrap()),
422
    /// #             string: Cow::Borrowed(bincode::deserialize(data).unwrap()),
423
    /// #             owned: Vec::new(),
424
    /// #         }
425
    /// #     })
426
    /// # }
427
    ///
428
    /// #[derive(Yokeable)]
429
    /// struct Bar<'a> {
430
    ///     numbers: Cow<'a, [u8]>,
431
    ///     string: Cow<'a, str>,
432
    ///     owned: Vec<u8>,
433
    /// }
434
    ///
435
    /// // `load_object()` deserializes an object from a file
436
    /// let mut bar: Yoke<Bar, _> = load_object("filename.bincode");
437
    /// assert_eq!(bar.get().string, "hello");
438
    /// assert!(matches!(bar.get().string, Cow::Borrowed(_)));
439
    /// assert_eq!(&*bar.get().numbers, &[0x68, 0x65, 0x6c, 0x6c, 0x6f]);
440
    /// assert!(matches!(bar.get().numbers, Cow::Borrowed(_)));
441
    /// assert_eq!(&*bar.get().owned, &[]);
442
    ///
443
    /// bar.with_mut(|bar| {
444
    ///     bar.string.to_mut().push_str(" world");
445
    ///     bar.owned.extend_from_slice(&[1, 4, 1, 5, 9]);
446
    /// });
447
    ///
448
    /// assert_eq!(bar.get().string, "hello world");
449
    /// assert!(matches!(bar.get().string, Cow::Owned(_)));
450
    /// assert_eq!(&*bar.get().owned, &[1, 4, 1, 5, 9]);
451
    /// // Unchanged and still Cow::Borrowed
452
    /// assert_eq!(&*bar.get().numbers, &[0x68, 0x65, 0x6c, 0x6c, 0x6f]);
453
    /// assert!(matches!(bar.get().numbers, Cow::Borrowed(_)));
454
    /// ```
455
0
    pub fn with_mut<'a, F>(&'a mut self, f: F)
456
0
    where
457
0
        F: 'static + for<'b> FnOnce(&'b mut <Y as Yokeable<'a>>::Output),
458
    {
459
0
        self.yokeable.transform_mut(f);
460
0
    }
461
462
    /// Mutate the stored [`Yokeable`] data, and return `'static` data (possibly just `()`).
463
    ///
464
    /// See [`Yokeable::transform_mut()`] for why this operation is safe, noting that no
465
    /// `'static`.
466
    ///
467
    /// ### Will be removed
468
    /// This method will be removed on the next breaking release of `yoke`, when the callback of
469
    /// [`Yoke::with_mut`] will gain the ability to return any `R: 'static` and supersede this
470
    /// method.
471
0
    pub fn with_mut_return<'a, F, R>(&'a mut self, f: F) -> R
472
0
    where
473
0
        F: 'static + for<'b> FnOnce(&'b mut <Y as Yokeable<'a>>::Output) -> R,
474
0
        R: 'static,
475
    {
476
0
        utils::transform_mut_yokeable(&mut *self.yokeable, f)
477
0
    }
478
479
    /// Helper function allowing one to wrap the cart type `C` in an `Option<T>`.
480
    #[inline]
481
0
    pub fn wrap_cart_in_option(self) -> Yoke<Y, Option<C>> {
482
        // Safety: the cart is preserved (since it is just wrapped into a Some),
483
        // so any data it owns is too.
484
0
        unsafe { self.replace_cart(Some) }
485
0
    }
486
}
487
488
impl<Y: for<'a> Yokeable<'a>> Yoke<Y, ()> {
489
    /// Construct a new [`Yoke`] from static data. There will be no
490
    /// references to `cart` here since [`Yokeable`]s are `'static`,
491
    /// this is good for e.g. constructing fully owned
492
    /// [`Yoke`]s with no internal borrowing.
493
    ///
494
    /// This is similar to [`Yoke::new_owned()`] but it does not allow you to
495
    /// mix the [`Yoke`] with borrowed data. This is primarily useful
496
    /// for using [`Yoke`] in generic scenarios.
497
    ///
498
    /// # Example
499
    ///
500
    /// ```rust
501
    /// # use yoke::Yoke;
502
    /// # use std::borrow::Cow;
503
    ///
504
    /// let owned: Cow<str> = "hello".to_owned().into();
505
    /// // this yoke can be intermingled with actually-borrowed Yokes
506
    /// let yoke: Yoke<Cow<str>, ()> = Yoke::new_always_owned(owned);
507
    ///
508
    /// assert_eq!(yoke.get(), "hello");
509
    /// ```
510
0
    pub fn new_always_owned(yokeable: Y) -> Self {
511
0
        Self {
512
0
            // Safety note: this `yokeable` certainly does not reference data owned by (), so we do
513
0
            // not have to worry about when the `yokeable` is dropped.
514
0
            yokeable: KindaSortaDangling::new(yokeable),
515
0
            cart: (),
516
0
        }
517
0
    }
518
519
    /// Obtain the yokeable out of a `Yoke<Y, ()>`
520
    ///
521
    /// For most `Yoke` types this would be unsafe but it's
522
    /// fine for `Yoke<Y, ()>` since there are no actual internal
523
    /// references
524
0
    pub fn into_yokeable(self) -> Y {
525
        // Safety note: since `yokeable` cannot reference data owned by `()`, this is certainly
526
        // safe.
527
0
        self.yokeable.into_inner()
528
0
    }
529
}
530
531
// C does not need to be StableDeref here, if the yoke was constructed it's valid,
532
// and new_owned() doesn't construct a yokeable that uses references,
533
impl<Y: for<'a> Yokeable<'a>, C> Yoke<Y, Option<C>> {
534
    /// Construct a new [`Yoke`] from static data. There will be no
535
    /// references to `cart` here since [`Yokeable`]s are `'static`,
536
    /// this is good for e.g. constructing fully owned
537
    /// [`Yoke`]s with no internal borrowing.
538
    ///
539
    /// This can be paired with [`Yoke:: wrap_cart_in_option()`] to mix owned
540
    /// and borrowed data.
541
    ///
542
    /// If you do not wish to pair this with borrowed data, [`Yoke::new_always_owned()`] can
543
    /// be used to get a [`Yoke`] API on always-owned data.
544
    ///
545
    /// # Example
546
    ///
547
    /// ```rust
548
    /// # use yoke::Yoke;
549
    /// # use std::borrow::Cow;
550
    /// # use std::rc::Rc;
551
    ///
552
    /// let owned: Cow<str> = "hello".to_owned().into();
553
    /// // this yoke can be intermingled with actually-borrowed Yokes
554
    /// let yoke: Yoke<Cow<str>, Option<Rc<[u8]>>> = Yoke::new_owned(owned);
555
    ///
556
    /// assert_eq!(yoke.get(), "hello");
557
    /// ```
558
0
    pub const fn new_owned(yokeable: Y) -> Self {
559
0
        Self {
560
0
            // Safety note: this `yokeable` is known not to borrow from the cart.
561
0
            yokeable: KindaSortaDangling::new(yokeable),
562
0
            cart: None,
563
0
        }
564
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
565
566
    /// Obtain the yokeable out of a `Yoke<Y, Option<C>>` if possible.
567
    ///
568
    /// If the cart is `None`, this returns `Ok`, but if the cart is `Some`,
569
    /// this returns `self` as an error.
570
0
    pub fn try_into_yokeable(self) -> Result<Y, Self> {
571
        // Safety: if the cart is None there is no way for the yokeable to
572
        // have references into it because of the cart invariant.
573
0
        match self.cart {
574
0
            Some(_) => Err(self),
575
0
            None => Ok(self.yokeable.into_inner()),
576
        }
577
0
    }
578
}
579
580
impl<Y: for<'a> Yokeable<'a>, C: CartablePointerLike> Yoke<Y, Option<C>> {
581
    /// Converts a `Yoke<Y, Option<C>>` to `Yoke<Y, CartableOptionPointer<C>>`
582
    /// for better niche optimization when stored as a field.
583
    ///
584
    /// # Examples
585
    ///
586
    /// ```
587
    /// use std::borrow::Cow;
588
    /// use yoke::Yoke;
589
    ///
590
    /// let yoke: Yoke<Cow<[u8]>, Box<Vec<u8>>> =
591
    ///     Yoke::attach_to_cart(vec![10, 20, 30].into(), |c| c.into());
592
    ///
593
    /// let yoke_option = yoke.wrap_cart_in_option();
594
    /// let yoke_option_pointer = yoke_option.convert_cart_into_option_pointer();
595
    /// ```
596
    ///
597
    /// The niche improves stack sizes:
598
    ///
599
    /// ```
600
    /// use yoke::Yoke;
601
    /// use yoke::cartable_ptr::CartableOptionPointer;
602
    /// use std::mem::size_of;
603
    /// use std::rc::Rc;
604
    ///
605
    /// // The data struct is 6 words:
606
    /// # #[derive(yoke::Yokeable)]
607
    /// # struct MyDataStruct<'a> {
608
    /// #     _s: (usize, usize, usize, usize),
609
    /// #     _p: &'a str,
610
    /// # }
611
    /// const W: usize = core::mem::size_of::<usize>();
612
    /// assert_eq!(W * 6, size_of::<MyDataStruct>());
613
    ///
614
    /// // An enum containing the data struct with an `Option<Rc>` cart is 8 words:
615
    /// enum StaticOrYoke1 {
616
    ///     Static(&'static MyDataStruct<'static>),
617
    ///     Yoke(Yoke<MyDataStruct<'static>, Option<Rc<String>>>),
618
    /// }
619
    /// assert_eq!(W * 8, size_of::<StaticOrYoke1>());
620
    ///
621
    /// // When using `CartableOptionPointer``, we need only 7 words for the same behavior:
622
    /// enum StaticOrYoke2 {
623
    ///     Static(&'static MyDataStruct<'static>),
624
    ///     Yoke(Yoke<MyDataStruct<'static>, CartableOptionPointer<Rc<String>>>),
625
    /// }
626
    /// assert_eq!(W * 7, size_of::<StaticOrYoke2>());
627
    /// ```
628
    #[inline]
629
0
    pub fn convert_cart_into_option_pointer(self) -> Yoke<Y, CartableOptionPointer<C>> {
630
0
        match self.cart {
631
0
            Some(cart) => Yoke {
632
0
                // Safety note: CartableOptionPointer::from_cartable only wraps the `cart`,
633
0
                // so the data referenced by the yokeable is still live.
634
0
                yokeable: self.yokeable,
635
0
                cart: CartableOptionPointer::from_cartable(cart),
636
0
            },
637
0
            None => Yoke {
638
0
                // Safety note: this Yokeable cannot refer to any data since self.cart is None.
639
0
                yokeable: self.yokeable,
640
0
                cart: CartableOptionPointer::none(),
641
0
            },
642
        }
643
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
644
}
645
646
impl<Y: for<'a> Yokeable<'a>, C: CartablePointerLike> Yoke<Y, CartableOptionPointer<C>> {
647
    /// Obtain the yokeable out of a `Yoke<Y, CartableOptionPointer<C>>` if possible.
648
    ///
649
    /// If the cart is `None`, this returns `Ok`, but if the cart is `Some`,
650
    /// this returns `self` as an error.
651
    #[inline]
652
0
    pub fn try_into_yokeable(self) -> Result<Y, Self> {
653
0
        if self.cart.is_none() {
654
0
            Ok(self.yokeable.into_inner())
655
        } else {
656
0
            Err(self)
657
        }
658
0
    }
659
}
660
661
/// This trait marks cart types that do not change source on cloning
662
///
663
/// This is conceptually similar to [`stable_deref_trait::CloneStableDeref`],
664
/// however [`stable_deref_trait::CloneStableDeref`] is not (and should not) be
665
/// implemented on [`Option`] (since it's not [`Deref`]). [`CloneableCart`] essentially is
666
/// "if there _is_ data to borrow from here, cloning the cart gives you an additional
667
/// handle to the same data".
668
///
669
/// # Safety
670
/// This trait is safe to implement on `StableDeref` types which, once `Clone`d, point to the same underlying data and retain ownership.
671
///
672
/// This trait can also be implemented on aggregates of such types like `Option<T: CloneableCart>` and `(T: CloneableCart, U: CloneableCart)`.
673
///
674
/// Essentially, all data that could be referenced by a Yokeable (i.e. data that is referenced via a StableDeref) must retain the same
675
/// pointer and ownership semantics once cloned.
676
pub unsafe trait CloneableCart: Clone {}
677
678
#[cfg(feature = "alloc")]
679
// Safety: Rc<T> implements CloneStableDeref.
680
unsafe impl<T: ?Sized> CloneableCart for Rc<T> {}
681
#[cfg(feature = "alloc")]
682
// Safety: Arc<T> implements CloneStableDeref.
683
unsafe impl<T: ?Sized> CloneableCart for Arc<T> {}
684
// Safety: Option<T> cannot deref to anything that T doesn't already deref to.
685
unsafe impl<T: CloneableCart> CloneableCart for Option<T> {}
686
// Safety: &'a T is indeed StableDeref, and cloning it refers to the same data.
687
// &'a T does not own in the first place, so ownership is preserved.
688
unsafe impl<'a, T: ?Sized> CloneableCart for &'a T {}
689
// Safety: () cannot deref to anything.
690
unsafe impl CloneableCart for () {}
691
692
/// Clone requires that the cart type `C` derefs to the same address after it is cloned. This works for
693
/// Rc, Arc, and &'a T.
694
///
695
/// For other cart types, clone `.backing_cart()` and re-use `.attach_to_cart()`; however, doing
696
/// so may lose mutations performed via `.with_mut()`.
697
///
698
/// Cloning a `Yoke` is often a cheap operation requiring no heap allocations, in much the same
699
/// way that cloning an `Rc` is a cheap operation. However, if the `yokeable` contains owned data
700
/// (e.g., from `.with_mut()`), that data will need to be cloned.
701
impl<Y: for<'a> Yokeable<'a>, C: CloneableCart> Clone for Yoke<Y, C>
702
where
703
    for<'a> <Y as Yokeable<'a>>::Output: Clone,
704
{
705
0
    fn clone(&self) -> Self {
706
        // We have an &T not a T, and we can clone T
707
0
        let this = self.get().clone();
708
0
        Yoke {
709
0
            yokeable: KindaSortaDangling::new(
710
0
                // Safety: C being a CloneableCart guarantees that the data referenced by the
711
0
                // `yokeable` is kept alive by the clone of the cart.
712
0
                unsafe { Y::make(this) },
713
0
            ),
714
0
            cart: self.cart.clone(),
715
0
        }
716
0
    }
717
}
718
719
#[test]
720
fn test_clone() {
721
    let local_data = "foo".to_owned();
722
    let y1 = Yoke::<alloc::borrow::Cow<'static, str>, Rc<String>>::attach_to_zero_copy_cart(
723
        Rc::new(local_data),
724
    );
725
726
    // Test basic clone
727
    let y2 = y1.clone();
728
    assert_eq!(y1.get(), "foo");
729
    assert_eq!(y2.get(), "foo");
730
731
    // Test clone with mutation on target
732
    let mut y3 = y1.clone();
733
    y3.with_mut(|y| {
734
        y.to_mut().push_str("bar");
735
    });
736
    assert_eq!(y1.get(), "foo");
737
    assert_eq!(y2.get(), "foo");
738
    assert_eq!(y3.get(), "foobar");
739
740
    // Test that mutations on source do not affect target
741
    let y4 = y3.clone();
742
    y3.with_mut(|y| {
743
        y.to_mut().push_str("baz");
744
    });
745
    assert_eq!(y1.get(), "foo");
746
    assert_eq!(y2.get(), "foo");
747
    assert_eq!(y3.get(), "foobarbaz");
748
    assert_eq!(y4.get(), "foobar");
749
}
750
751
impl<Y: for<'a> Yokeable<'a>, C> Yoke<Y, C> {
752
    /// Allows one to "project" a yoke to perform a transformation on the data, potentially
753
    /// looking at a subfield, and producing a new yoke. This will move cart, and the provided
754
    /// transformation is only allowed to use data known to be borrowed from the cart.
755
    ///
756
    /// If producing the new [`Yokeable`] `P` requires access to the cart in addition to the old
757
    /// `Y`, then [`Yoke::map_with_cart`] can be used if the cart satisfies additional constraints.
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
    /// #         unsafe { mem::transmute(from) }
807
    /// #     }
808
    /// #
809
    /// #     fn transform_mut<F>(&'a mut self, f: F)
810
    /// #     where
811
    /// #         F: 'static + FnOnce(&'a mut Self::Output),
812
    /// #     {
813
    /// #         unsafe { f(mem::transmute(self)) }
814
    /// #     }
815
    /// # }
816
    /// ```
817
    //
818
    // Safety docs can be found at the end of the file.
819
0
    pub fn map_project<P, F>(self, f: F) -> Yoke<P, C>
820
0
    where
821
0
        P: for<'a> Yokeable<'a>,
822
0
        F: for<'a> FnOnce(
823
0
            <Y as Yokeable<'a>>::Output,
824
0
            PhantomData<&'a ()>,
825
0
        ) -> <P as Yokeable<'a>>::Output,
826
    {
827
0
        let p = f(self.yokeable.into_inner().transform_owned(), PhantomData);
828
0
        Yoke {
829
0
            yokeable: KindaSortaDangling::new(
830
0
                // Safety: the resulting `yokeable` is dropped before the `cart` because
831
0
                // of the Yoke invariant. See the safety docs below for the justification of why
832
0
                // yokeable could only borrow from the Cart.
833
0
                unsafe { P::make(p) },
834
0
            ),
835
0
            cart: self.cart,
836
0
        }
837
0
    }
838
839
    /// This is similar to [`Yoke::map_project`], however it does not move
840
    /// [`Self`] and instead clones the cart (only if the cart is a [`CloneableCart`])
841
    ///
842
    /// This is a bit more efficient than cloning the [`Yoke`] and then calling [`Yoke::map_project`]
843
    /// because then it will not clone fields that are going to be discarded.
844
0
    pub fn map_project_cloned<'this, P, F>(&'this self, f: F) -> Yoke<P, C>
845
0
    where
846
0
        P: for<'a> Yokeable<'a>,
847
0
        C: CloneableCart,
848
0
        F: for<'a> FnOnce(
849
0
            &'this <Y as Yokeable<'a>>::Output,
850
0
            PhantomData<&'a ()>,
851
0
        ) -> <P as Yokeable<'a>>::Output,
852
    {
853
0
        let p = f(self.get(), PhantomData);
854
0
        Yoke {
855
0
            yokeable: KindaSortaDangling::new(
856
0
                // Safety: the resulting `yokeable` is dropped before the `cart` because
857
0
                // of the Yoke invariant. See the safety docs below for the justification of why
858
0
                // yokeable could only borrow from the Cart.
859
0
                unsafe { P::make(p) },
860
0
            ),
861
0
            cart: self.cart.clone(),
862
0
        }
863
0
    }
864
865
    /// This is similar to [`Yoke::map_project`], however it can also bubble up an error
866
    /// from the callback.
867
    ///
868
    /// ```
869
    /// # use std::rc::Rc;
870
    /// # use yoke::Yoke;
871
    /// # use std::str::{self, Utf8Error};
872
    /// #
873
    /// fn slice(
874
    ///     y: Yoke<&'static [u8], Rc<[u8]>>,
875
    /// ) -> Result<Yoke<&'static str, Rc<[u8]>>, Utf8Error> {
876
    ///     y.try_map_project(move |bytes, _| str::from_utf8(bytes))
877
    /// }
878
    /// ```
879
    ///
880
    /// This can also be used to create a yoke for a subfield
881
    ///
882
    /// ```
883
    /// # use yoke::{Yoke, Yokeable};
884
    /// # use std::mem;
885
    /// # use std::rc::Rc;
886
    /// # use std::str::{self, Utf8Error};
887
    /// #
888
    /// // also safely implements Yokeable<'a>
889
    /// struct Bar<'a> {
890
    ///     bytes_1: &'a [u8],
891
    ///     string_2: &'a str,
892
    /// }
893
    ///
894
    /// fn map_project_string_1(
895
    ///     bar: Yoke<Bar<'static>, Rc<[u8]>>,
896
    /// ) -> Result<Yoke<&'static str, Rc<[u8]>>, Utf8Error> {
897
    ///     bar.try_map_project(|bar, _| str::from_utf8(bar.bytes_1))
898
    /// }
899
    ///
900
    /// #
901
    /// # unsafe impl<'a> Yokeable<'a> for Bar<'static> {
902
    /// #     type Output = Bar<'a>;
903
    /// #     fn transform(&'a self) -> &'a Bar<'a> {
904
    /// #         self
905
    /// #     }
906
    /// #
907
    /// #     fn transform_owned(self) -> Bar<'a> {
908
    /// #         // covariant lifetime cast, can be done safely
909
    /// #         self
910
    /// #     }
911
    /// #
912
    /// #     unsafe fn make(from: Bar<'a>) -> Self {
913
    /// #         unsafe { mem::transmute(from) }
914
    /// #     }
915
    /// #
916
    /// #     fn transform_mut<F>(&'a mut self, f: F)
917
    /// #     where
918
    /// #         F: 'static + FnOnce(&'a mut Self::Output),
919
    /// #     {
920
    /// #         unsafe { f(mem::transmute(self)) }
921
    /// #     }
922
    /// # }
923
    /// ```
924
0
    pub fn try_map_project<P, F, E>(self, f: F) -> Result<Yoke<P, C>, E>
925
0
    where
926
0
        P: for<'a> Yokeable<'a>,
927
0
        F: for<'a> FnOnce(
928
0
            <Y as Yokeable<'a>>::Output,
929
0
            PhantomData<&'a ()>,
930
0
        ) -> Result<<P as Yokeable<'a>>::Output, E>,
931
    {
932
0
        let p = f(self.yokeable.into_inner().transform_owned(), PhantomData)?;
933
0
        Ok(Yoke {
934
0
            yokeable: KindaSortaDangling::new(
935
0
                // Safety: the resulting `yokeable` is dropped before the `cart` because
936
0
                // of the Yoke invariant. See the safety docs below for the justification of why
937
0
                // yokeable could only borrow from the Cart.
938
0
                unsafe { P::make(p) },
939
0
            ),
940
0
            cart: self.cart,
941
0
        })
942
0
    }
943
944
    /// This is similar to [`Yoke::try_map_project`], however it does not move
945
    /// [`Self`] and instead clones the cart (only if the cart is a [`CloneableCart`])
946
    ///
947
    /// This is a bit more efficient than cloning the [`Yoke`] and then calling [`Yoke::map_project`]
948
    /// because then it will not clone fields that are going to be discarded.
949
0
    pub fn try_map_project_cloned<'this, P, F, E>(&'this self, f: F) -> Result<Yoke<P, C>, E>
950
0
    where
951
0
        P: for<'a> Yokeable<'a>,
952
0
        C: CloneableCart,
953
0
        F: for<'a> FnOnce(
954
0
            &'this <Y as Yokeable<'a>>::Output,
955
0
            PhantomData<&'a ()>,
956
0
        ) -> Result<<P as Yokeable<'a>>::Output, E>,
957
    {
958
0
        let p = f(self.get(), PhantomData)?;
959
0
        Ok(Yoke {
960
0
            yokeable: KindaSortaDangling::new(
961
0
                // Safety: the resulting `yokeable` is dropped before the `cart` because
962
0
                // of the Yoke invariant. See the safety docs below for the justification of why
963
0
                // yokeable could only borrow from the Cart.
964
0
                unsafe { P::make(p) },
965
0
            ),
966
0
            cart: self.cart.clone(),
967
0
        })
968
0
    }
969
    /// This is similar to [`Yoke::map_project`], but it works around older versions
970
    /// of Rust not being able to use `FnOnce` by using an explicit capture input.
971
    /// See [#1061](https://github.com/unicode-org/icu4x/issues/1061).
972
    ///
973
    /// See the docs of [`Yoke::map_project`] for how this works.
974
0
    pub fn map_project_with_explicit_capture<P, T>(
975
0
        self,
976
0
        capture: T,
977
0
        f: for<'a> fn(
978
0
            <Y as Yokeable<'a>>::Output,
979
0
            capture: T,
980
0
            PhantomData<&'a ()>,
981
0
        ) -> <P as Yokeable<'a>>::Output,
982
0
    ) -> Yoke<P, C>
983
0
    where
984
0
        P: for<'a> Yokeable<'a>,
985
    {
986
0
        let p = f(
987
0
            self.yokeable.into_inner().transform_owned(),
988
0
            capture,
989
0
            PhantomData,
990
0
        );
991
0
        Yoke {
992
0
            yokeable: KindaSortaDangling::new(
993
0
                // Safety: the resulting `yokeable` is dropped before the `cart` because
994
0
                // of the Yoke invariant. See the safety docs below for the justification of why
995
0
                // yokeable could only borrow from the Cart.
996
0
                unsafe { P::make(p) },
997
0
            ),
998
0
            cart: self.cart,
999
0
        }
1000
0
    }
1001
1002
    /// This is similar to [`Yoke::map_project_cloned`], but it works around older versions
1003
    /// of Rust not being able to use `FnOnce` by using an explicit capture input.
1004
    /// See [#1061](https://github.com/unicode-org/icu4x/issues/1061).
1005
    ///
1006
    /// See the docs of [`Yoke::map_project_cloned`] for how this works.
1007
0
    pub fn map_project_cloned_with_explicit_capture<'this, P, T>(
1008
0
        &'this self,
1009
0
        capture: T,
1010
0
        f: for<'a> fn(
1011
0
            &'this <Y as Yokeable<'a>>::Output,
1012
0
            capture: T,
1013
0
            PhantomData<&'a ()>,
1014
0
        ) -> <P as Yokeable<'a>>::Output,
1015
0
    ) -> Yoke<P, C>
1016
0
    where
1017
0
        P: for<'a> Yokeable<'a>,
1018
0
        C: CloneableCart,
1019
    {
1020
0
        let p = f(self.get(), capture, PhantomData);
1021
0
        Yoke {
1022
0
            yokeable: KindaSortaDangling::new(
1023
0
                // Safety: the resulting `yokeable` is dropped before the `cart` because
1024
0
                // of the Yoke invariant. See the safety docs below for the justification of why
1025
0
                // yokeable could only borrow from the Cart.
1026
0
                unsafe { P::make(p) },
1027
0
            ),
1028
0
            cart: self.cart.clone(),
1029
0
        }
1030
0
    }
1031
1032
    /// This is similar to [`Yoke::try_map_project`], but it works around older versions
1033
    /// of Rust not being able to use `FnOnce` by using an explicit capture input.
1034
    /// See [#1061](https://github.com/unicode-org/icu4x/issues/1061).
1035
    ///
1036
    /// See the docs of [`Yoke::try_map_project`] for how this works.
1037
    #[expect(clippy::type_complexity)]
1038
0
    pub fn try_map_project_with_explicit_capture<P, T, E>(
1039
0
        self,
1040
0
        capture: T,
1041
0
        f: for<'a> fn(
1042
0
            <Y as Yokeable<'a>>::Output,
1043
0
            capture: T,
1044
0
            PhantomData<&'a ()>,
1045
0
        ) -> Result<<P as Yokeable<'a>>::Output, E>,
1046
0
    ) -> Result<Yoke<P, C>, E>
1047
0
    where
1048
0
        P: for<'a> Yokeable<'a>,
1049
    {
1050
0
        let p = f(
1051
0
            self.yokeable.into_inner().transform_owned(),
1052
0
            capture,
1053
0
            PhantomData,
1054
0
        )?;
1055
0
        Ok(Yoke {
1056
0
            yokeable: KindaSortaDangling::new(
1057
0
                // Safety: the resulting `yokeable` is dropped before the `cart` because
1058
0
                // of the Yoke invariant. See the safety docs below for the justification of why
1059
0
                // yokeable could only borrow from the Cart.
1060
0
                unsafe { P::make(p) },
1061
0
            ),
1062
0
            cart: self.cart,
1063
0
        })
1064
0
    }
1065
1066
    /// This is similar to [`Yoke::try_map_project_cloned`], but it works around older versions
1067
    /// of Rust not being able to use `FnOnce` by using an explicit capture input.
1068
    /// See [#1061](https://github.com/unicode-org/icu4x/issues/1061).
1069
    ///
1070
    /// See the docs of [`Yoke::try_map_project_cloned`] for how this works.
1071
    #[expect(clippy::type_complexity)]
1072
0
    pub fn try_map_project_cloned_with_explicit_capture<'this, P, T, E>(
1073
0
        &'this self,
1074
0
        capture: T,
1075
0
        f: for<'a> fn(
1076
0
            &'this <Y as Yokeable<'a>>::Output,
1077
0
            capture: T,
1078
0
            PhantomData<&'a ()>,
1079
0
        ) -> Result<<P as Yokeable<'a>>::Output, E>,
1080
0
    ) -> Result<Yoke<P, C>, E>
1081
0
    where
1082
0
        P: for<'a> Yokeable<'a>,
1083
0
        C: CloneableCart,
1084
    {
1085
0
        let p = f(self.get(), capture, PhantomData)?;
1086
0
        Ok(Yoke {
1087
0
            yokeable: KindaSortaDangling::new(
1088
0
                // Safety: the resulting `yokeable` is dropped before the `cart` because
1089
0
                // of the Yoke invariant. See the safety docs below for the justification of why
1090
0
                // yokeable could only borrow from the Cart.
1091
0
                unsafe { P::make(p) },
1092
0
            ),
1093
0
            cart: self.cart.clone(),
1094
0
        })
1095
0
    }
1096
}
1097
1098
impl<Y: for<'a> Yokeable<'a>, C: StableDeref> Yoke<Y, C>
1099
where
1100
    <C as Deref>::Target: 'static,
1101
{
1102
    /// Allows one to produce a new yoke from both the cart and the old yoke. This will move the
1103
    /// cart, and the provided transformation is only allowed to use data known to be borrowed from
1104
    /// the cart.
1105
    ///
1106
    /// If access to the old [`Yokeable`] `Y` is sufficient to produce the new [`Yokeable`] `P`,
1107
    /// then [`Yoke::map_project`] should be preferred, as `map_with_cart` places additional
1108
    /// constraints on the cart.
1109
    ///
1110
    /// This can be used, for example, to transform data between two formats, one of which contains
1111
    /// more data:
1112
    ///
1113
    /// ```
1114
    /// # use yoke::{Yoke, Yokeable};
1115
    /// # use std::mem;
1116
    /// # use std::rc::Rc;
1117
    /// #
1118
    /// // Both structs have `first_line`, which won't need to be recomputed in `map_with_cart`.
1119
    /// // They also safely implement `Yokeable<'a>`
1120
    /// struct Foo<'a> {
1121
    ///     first_line: Option<&'a str>,
1122
    /// }
1123
    /// struct Bar<'a> {
1124
    ///     first_line: Option<&'a str>,
1125
    ///     last_line: Option<&'a str>,
1126
    /// }
1127
    ///
1128
    /// fn foo_to_bar(
1129
    ///     foo: Yoke<Foo<'static>, Rc<str>>,
1130
    /// ) -> Yoke<Bar<'static>, Rc<str>> {
1131
    ///     foo.map_with_cart(|foo, cart| {
1132
    ///         Bar {
1133
    ///             first_line: foo.first_line,
1134
    ///             last_line: cart.lines().next_back(),
1135
    ///         }
1136
    ///     })
1137
    /// }
1138
    ///
1139
    /// fn bar_to_foo(
1140
    ///     bar: Yoke<Bar<'static>, Rc<str>>,
1141
    /// ) -> Yoke<Foo<'static>, Rc<str>> {
1142
    ///     bar.map_project(|bar, _| {
1143
    ///         Foo {
1144
    ///             first_line: bar.first_line,
1145
    ///         }
1146
    ///     })
1147
    /// }
1148
    ///
1149
    /// #
1150
    /// # unsafe impl<'a> Yokeable<'a> for Foo<'static> {
1151
    /// #     type Output = Foo<'a>;
1152
    /// #     fn transform(&'a self) -> &'a Foo<'a> {
1153
    /// #         self
1154
    /// #     }
1155
    /// #
1156
    /// #     fn transform_owned(self) -> Foo<'a> {
1157
    /// #         // covariant lifetime cast, can be done safely
1158
    /// #         self
1159
    /// #     }
1160
    /// #
1161
    /// #     unsafe fn make(from: Foo<'a>) -> Self {
1162
    /// #         unsafe { mem::transmute(from) }
1163
    /// #     }
1164
    /// #
1165
    /// #     fn transform_mut<F>(&'a mut self, f: F)
1166
    /// #     where
1167
    /// #         F: 'static + FnOnce(&'a mut Self::Output),
1168
    /// #     {
1169
    /// #         unsafe { f(mem::transmute(self)) }
1170
    /// #     }
1171
    /// # }
1172
    /// #
1173
    /// # unsafe impl<'a> Yokeable<'a> for Bar<'static> {
1174
    /// #     type Output = Bar<'a>;
1175
    /// #     fn transform(&'a self) -> &'a Bar<'a> {
1176
    /// #         self
1177
    /// #     }
1178
    /// #
1179
    /// #     fn transform_owned(self) -> Bar<'a> {
1180
    /// #         // covariant lifetime cast, can be done safely
1181
    /// #         self
1182
    /// #     }
1183
    /// #
1184
    /// #     unsafe fn make(from: Bar<'a>) -> Self {
1185
    /// #         unsafe { mem::transmute(from) }
1186
    /// #     }
1187
    /// #
1188
    /// #     fn transform_mut<F>(&'a mut self, f: F)
1189
    /// #     where
1190
    /// #         F: 'static + FnOnce(&'a mut Self::Output),
1191
    /// #     {
1192
    /// #         unsafe { f(mem::transmute(self)) }
1193
    /// #     }
1194
    /// # }
1195
    /// ```
1196
    //
1197
    // Safety docs can be found at the end of the file.
1198
0
    pub fn map_with_cart<P, F>(self, f: F) -> Yoke<P, C>
1199
0
    where
1200
0
        P: for<'a> Yokeable<'a>,
1201
0
        F: for<'a> FnOnce(
1202
0
            <Y as Yokeable<'a>>::Output,
1203
0
            &'a <C as Deref>::Target,
1204
0
        ) -> <P as Yokeable<'a>>::Output,
1205
0
        <C as Deref>::Target: 'static,
1206
    {
1207
0
        let p = f(
1208
0
            self.yokeable.into_inner().transform_owned(),
1209
0
            self.cart.deref(),
1210
0
        );
1211
0
        Yoke {
1212
0
            yokeable: KindaSortaDangling::new(
1213
0
                // Safety: the resulting `yokeable` is dropped before the `cart` because
1214
0
                // of the Yoke invariant. See the safety docs below for the justification of why
1215
0
                // yokeable could only borrow from the Cart.
1216
0
                unsafe { P::make(p) },
1217
0
            ),
1218
0
            cart: self.cart,
1219
0
        }
1220
0
    }
1221
1222
    /// This is similar to [`Yoke::map_with_cart`], but it does not move [`Self`] and instead
1223
    /// clones the cart (only if the cart is a [`CloneableCart`]).
1224
    ///
1225
    /// This is a bit more efficient than cloning the [`Yoke`] and then calling
1226
    /// [`Yoke::map_with_cart`] because it will not clone fields that are going to be discarded.
1227
    ///
1228
    /// If access to the old [`Yokeable`] `Y` is sufficient to produce the new [`Yokeable`] `P`,
1229
    /// then [`Yoke::map_project_cloned`] should be preferred, as `map_with_cart_cloned` places
1230
    /// additional constraints on the cart.
1231
0
    pub fn map_with_cart_cloned<'this, P, F>(&'this self, f: F) -> Yoke<P, C>
1232
0
    where
1233
0
        P: for<'a> Yokeable<'a>,
1234
0
        F: for<'a> FnOnce(
1235
0
            &'this <Y as Yokeable<'a>>::Output,
1236
0
            &'a <C as Deref>::Target,
1237
0
        ) -> <P as Yokeable<'a>>::Output,
1238
0
        C: CloneableCart,
1239
0
        <C as Deref>::Target: 'static,
1240
    {
1241
0
        let p = f(self.get(), self.cart.deref());
1242
0
        Yoke {
1243
0
            yokeable: KindaSortaDangling::new(
1244
0
                // Safety: the resulting `yokeable` is dropped before the `cart` because
1245
0
                // of the Yoke invariant. See the safety docs below for the justification of why
1246
0
                // yokeable could only borrow from the Cart.
1247
0
                unsafe { P::make(p) },
1248
0
            ),
1249
0
            cart: self.cart.clone(),
1250
0
        }
1251
0
    }
1252
1253
    /// This is similar to [`Yoke::map_with_cart`], but it can also bubble up an error
1254
    /// from the callback.
1255
    ///
1256
    /// If access to the old [`Yokeable`] `Y` is sufficient to produce the new [`Yokeable`] `P`,
1257
    /// then [`Yoke::try_map_project`] should be preferred, as `try_map_with_cart` places
1258
    /// additional constraints on the cart.
1259
    ///
1260
    /// ```
1261
    /// # use std::rc::Rc;
1262
    /// # use yoke::Yoke;
1263
    /// # use std::str::{self, Utf8Error};
1264
    /// #
1265
    /// // Implements `Yokeable`
1266
    /// type P<'a> = (&'a str, Option<&'a u8>);
1267
    ///
1268
    /// fn slice(
1269
    ///     y: Yoke<&'static [u8], Rc<[u8]>>,
1270
    /// ) -> Result<Yoke<P<'static>, Rc<[u8]>>, Utf8Error> {
1271
    ///     y.try_map_with_cart(move |bytes, cart| {
1272
    ///         Ok((str::from_utf8(bytes)?, bytes.first()))
1273
    ///     })
1274
    /// }
1275
    /// ```
1276
0
    pub fn try_map_with_cart<P, F, E>(self, f: F) -> Result<Yoke<P, C>, E>
1277
0
    where
1278
0
        P: for<'a> Yokeable<'a>,
1279
0
        F: for<'a> FnOnce(
1280
0
            <Y as Yokeable<'a>>::Output,
1281
0
            &'a <C as Deref>::Target,
1282
0
        ) -> Result<<P as Yokeable<'a>>::Output, E>,
1283
0
        <C as Deref>::Target: 'static,
1284
    {
1285
0
        let p = f(
1286
0
            self.yokeable.into_inner().transform_owned(),
1287
0
            self.cart.deref(),
1288
0
        )?;
1289
0
        Ok(Yoke {
1290
0
            yokeable: KindaSortaDangling::new(
1291
0
                // Safety: the resulting `yokeable` is dropped before the `cart` because
1292
0
                // of the Yoke invariant. See the safety docs below for the justification of why
1293
0
                // yokeable could only borrow from the Cart.
1294
0
                unsafe { P::make(p) },
1295
0
            ),
1296
0
            cart: self.cart,
1297
0
        })
1298
0
    }
1299
1300
    /// This is similar to [`Yoke::try_map_with_cart`], but it does not move [`Self`] and instead
1301
    /// clones the cart (only if the cart is a [`CloneableCart`]).
1302
    ///
1303
    /// This is a bit more efficient than cloning the [`Yoke`] and then calling
1304
    /// [`Yoke::try_map_with_cart`] because it will not clone fields that are going to be discarded.
1305
    ///
1306
    /// If access to the old [`Yokeable`] `Y` is sufficient to producethe new [`Yokeable`] `P`,
1307
    /// then [`Yoke::try_map_project_cloned`] should be preferred, as `try_map_with_cart_cloned`
1308
    /// places additional constraints on the cart.
1309
0
    pub fn try_map_with_cart_cloned<'this, P, F, E>(&'this self, f: F) -> Result<Yoke<P, C>, E>
1310
0
    where
1311
0
        P: for<'a> Yokeable<'a>,
1312
0
        C: CloneableCart,
1313
0
        F: for<'a> FnOnce(
1314
0
            &'this <Y as Yokeable<'a>>::Output,
1315
0
            &'a <C as Deref>::Target,
1316
0
        ) -> Result<<P as Yokeable<'a>>::Output, E>,
1317
0
        <C as Deref>::Target: 'static,
1318
    {
1319
0
        let p = f(self.get(), self.cart.deref())?;
1320
0
        Ok(Yoke {
1321
0
            yokeable: KindaSortaDangling::new(
1322
0
                // Safety: the resulting `yokeable` is dropped before the `cart` because
1323
0
                // of the Yoke invariant. See the safety docs below for the justification of why
1324
0
                // yokeable could only borrow from the Cart.
1325
0
                unsafe { P::make(p) },
1326
0
            ),
1327
0
            cart: self.cart.clone(),
1328
0
        })
1329
0
    }
1330
}
1331
1332
#[cfg(feature = "alloc")]
1333
impl<Y: for<'a> Yokeable<'a>, C: 'static + Sized> Yoke<Y, Rc<C>> {
1334
    /// Allows type-erasing the cart in a `Yoke<Y, Rc<C>>`.
1335
    ///
1336
    /// The yoke only carries around a cart type `C` for its destructor,
1337
    /// since it needs to be able to guarantee that its internal references
1338
    /// are valid for the lifetime of the Yoke. As such, the actual type of the
1339
    /// Cart is not very useful unless you wish to extract data out of it
1340
    /// via [`Yoke::backing_cart()`]. Erasing the cart allows for one to mix
1341
    /// [`Yoke`]s obtained from different sources.
1342
    ///
1343
    /// In case the cart type `C` is not already an `Rc<T>`, you can use
1344
    /// [`Yoke::wrap_cart_in_rc()`] to wrap it.
1345
    ///
1346
    /// ✨ *Enabled with the `alloc` Cargo feature.*
1347
    ///
1348
    /// # Example
1349
    ///
1350
    /// ```rust
1351
    /// use std::rc::Rc;
1352
    /// use yoke::erased::ErasedRcCart;
1353
    /// use yoke::Yoke;
1354
    ///
1355
    /// let buffer1: Rc<String> = Rc::new("   foo bar baz  ".into());
1356
    /// let buffer2: Box<String> = Box::new("  baz quux  ".into());
1357
    ///
1358
    /// let yoke1 =
1359
    ///     Yoke::<&'static str, _>::attach_to_cart(buffer1, |rc| rc.trim());
1360
    /// let yoke2 = Yoke::<&'static str, _>::attach_to_cart(buffer2, |b| b.trim());
1361
    ///
1362
    /// let erased1: Yoke<_, ErasedRcCart> = yoke1.erase_rc_cart();
1363
    /// // Wrap the Box in an Rc to make it compatible
1364
    /// let erased2: Yoke<_, ErasedRcCart> =
1365
    ///     yoke2.wrap_cart_in_rc().erase_rc_cart();
1366
    ///
1367
    /// // Now erased1 and erased2 have the same type!
1368
    /// ```
1369
    pub fn erase_rc_cart(self) -> Yoke<Y, ErasedRcCart> {
1370
        // Safety: safe because the cart is preserved, as it is just type-erased
1371
        unsafe { self.replace_cart(|c| c as ErasedRcCart) }
1372
    }
1373
}
1374
1375
#[cfg(feature = "alloc")]
1376
impl<Y: for<'a> Yokeable<'a>, C: 'static + Sized + Send + Sync> Yoke<Y, Arc<C>> {
1377
    /// Allows type-erasing the cart in a `Yoke<Y, Arc<C>>`.
1378
    ///
1379
    /// The yoke only carries around a cart type `C` for its destructor,
1380
    /// since it needs to be able to guarantee that its internal references
1381
    /// are valid for the lifetime of the Yoke. As such, the actual type of the
1382
    /// Cart is not very useful unless you wish to extract data out of it
1383
    /// via [`Yoke::backing_cart()`]. Erasing the cart allows for one to mix
1384
    /// [`Yoke`]s obtained from different sources.
1385
    ///
1386
    /// In case the cart type `C` is not already an `Arc<T>`, you can use
1387
    /// [`Yoke::wrap_cart_in_arc()`] to wrap it.
1388
    ///
1389
    /// ✨ *Enabled with the `alloc` Cargo feature.*
1390
    ///
1391
    /// # Example
1392
    ///
1393
    /// ```rust
1394
    /// use std::sync::Arc;
1395
    /// use yoke::erased::ErasedArcCart;
1396
    /// use yoke::Yoke;
1397
    ///
1398
    /// let buffer1: Arc<String> = Arc::new("   foo bar baz  ".into());
1399
    /// let buffer2: Box<String> = Box::new("  baz quux  ".into());
1400
    ///
1401
    /// let yoke1 =
1402
    ///     Yoke::<&'static str, _>::attach_to_cart(buffer1, |arc| arc.trim());
1403
    /// let yoke2 = Yoke::<&'static str, _>::attach_to_cart(buffer2, |b| b.trim());
1404
    ///
1405
    /// let erased1: Yoke<_, ErasedArcCart> = yoke1.erase_arc_cart();
1406
    /// // Wrap the Box in an Rc to make it compatible
1407
    /// let erased2: Yoke<_, ErasedArcCart> =
1408
    ///     yoke2.wrap_cart_in_arc().erase_arc_cart();
1409
    ///
1410
    /// // Now erased1 and erased2 have the same type!
1411
    /// ```
1412
    pub fn erase_arc_cart(self) -> Yoke<Y, ErasedArcCart> {
1413
        // Safety: safe because the cart is preserved, as it is just type-erased
1414
        unsafe { self.replace_cart(|c| c as ErasedArcCart) }
1415
    }
1416
}
1417
1418
#[cfg(feature = "alloc")]
1419
impl<Y: for<'a> Yokeable<'a>, C: 'static + Sized> Yoke<Y, Box<C>> {
1420
    /// Allows type-erasing the cart in a `Yoke<Y, Box<C>>`.
1421
    ///
1422
    /// The yoke only carries around a cart type `C` for its destructor,
1423
    /// since it needs to be able to guarantee that its internal references
1424
    /// are valid for the lifetime of the Yoke. As such, the actual type of the
1425
    /// Cart is not very useful unless you wish to extract data out of it
1426
    /// via [`Yoke::backing_cart()`]. Erasing the cart allows for one to mix
1427
    /// [`Yoke`]s obtained from different sources.
1428
    ///
1429
    /// In case the cart type `C` is not already `Box<T>`, you can use
1430
    /// [`Yoke::wrap_cart_in_box()`] to wrap it.
1431
    ///
1432
    /// ✨ *Enabled with the `alloc` Cargo feature.*
1433
    ///
1434
    /// # Example
1435
    ///
1436
    /// ```rust
1437
    /// use std::rc::Rc;
1438
    /// use yoke::erased::ErasedBoxCart;
1439
    /// use yoke::Yoke;
1440
    ///
1441
    /// let buffer1: Rc<String> = Rc::new("   foo bar baz  ".into());
1442
    /// let buffer2: Box<String> = Box::new("  baz quux  ".into());
1443
    ///
1444
    /// let yoke1 =
1445
    ///     Yoke::<&'static str, _>::attach_to_cart(buffer1, |rc| rc.trim());
1446
    /// let yoke2 = Yoke::<&'static str, _>::attach_to_cart(buffer2, |b| b.trim());
1447
    ///
1448
    /// // Wrap the Rc in an Box to make it compatible
1449
    /// let erased1: Yoke<_, ErasedBoxCart> =
1450
    ///     yoke1.wrap_cart_in_box().erase_box_cart();
1451
    /// let erased2: Yoke<_, ErasedBoxCart> = yoke2.erase_box_cart();
1452
    ///
1453
    /// // Now erased1 and erased2 have the same type!
1454
    /// ```
1455
    pub fn erase_box_cart(self) -> Yoke<Y, ErasedBoxCart> {
1456
        // Safety: safe because the cart is preserved, as it is just type-erased
1457
        unsafe { self.replace_cart(|c| c as ErasedBoxCart) }
1458
    }
1459
}
1460
1461
#[cfg(feature = "alloc")]
1462
impl<Y: for<'a> Yokeable<'a>, C> Yoke<Y, C> {
1463
    /// Helper function allowing one to wrap the cart type `C` in a `Box<T>`.
1464
    /// Can be paired with [`Yoke::erase_box_cart()`]
1465
    ///
1466
    /// ✨ *Enabled with the `alloc` Cargo feature.*
1467
    #[inline]
1468
    pub fn wrap_cart_in_box(self) -> Yoke<Y, Box<C>> {
1469
        // Safety: safe because the cart is preserved, as it is just wrapped.
1470
        unsafe { self.replace_cart(Box::new) }
1471
    }
1472
    /// Helper function allowing one to wrap the cart type `C` in an `Rc<T>`.
1473
    /// Can be paired with [`Yoke::erase_rc_cart()`], or generally used
1474
    /// to make the [`Yoke`] cloneable.
1475
    ///
1476
    /// ✨ *Enabled with the `alloc` Cargo feature.*
1477
    #[inline]
1478
    pub fn wrap_cart_in_rc(self) -> Yoke<Y, Rc<C>> {
1479
        // Safety: safe because the cart is preserved, as it is just wrapped
1480
        unsafe { self.replace_cart(Rc::new) }
1481
    }
1482
    /// Helper function allowing one to wrap the cart type `C` in an `Arc<T>`.
1483
    /// Can be paired with [`Yoke::erase_arc_cart()`], or generally used
1484
    /// to make the [`Yoke`] cloneable.
1485
    ///
1486
    /// ✨ *Enabled with the `alloc` Cargo feature.*
1487
    #[inline]
1488
    pub fn wrap_cart_in_arc(self) -> Yoke<Y, Arc<C>> {
1489
        // Safety: safe because the cart is preserved, as it is just wrapped
1490
        unsafe { self.replace_cart(Arc::new) }
1491
    }
1492
}
1493
1494
impl<Y: for<'a> Yokeable<'a>, C> Yoke<Y, C> {
1495
    /// Helper function allowing one to wrap the cart type `C` in an [`EitherCart`].
1496
    ///
1497
    /// This function wraps the cart into the `A` variant. To wrap it into the
1498
    /// `B` variant, use [`Self::wrap_cart_in_either_b()`].
1499
    ///
1500
    /// For an example, see [`EitherCart`].
1501
    #[inline]
1502
0
    pub fn wrap_cart_in_either_a<B>(self) -> Yoke<Y, EitherCart<C, B>> {
1503
        // Safety: safe because the cart is preserved, as it is just wrapped.
1504
0
        unsafe { self.replace_cart(EitherCart::A) }
1505
0
    }
1506
    /// Helper function allowing one to wrap the cart type `C` in an [`EitherCart`].
1507
    ///
1508
    /// This function wraps the cart into the `B` variant. To wrap it into the
1509
    /// `A` variant, use [`Self::wrap_cart_in_either_a()`].
1510
    ///
1511
    /// For an example, see [`EitherCart`].
1512
    #[inline]
1513
0
    pub fn wrap_cart_in_either_b<A>(self) -> Yoke<Y, EitherCart<A, C>> {
1514
        // Safety: safe because the cart is preserved, as it is just wrapped.
1515
0
        unsafe { self.replace_cart(EitherCart::B) }
1516
0
    }
1517
}
1518
1519
/// # Safety docs for *map_project*()
1520
///
1521
/// (Docs are on a private const to allow the use of compile_fail doctests)
1522
///
1523
/// This is safe to perform because of the choice of lifetimes on `f`, that is,
1524
/// `for<a> fn(<Y as Yokeable<'a>>::Output, &'a ()) -> <P as Yokeable<'a>>::Output`.
1525
///
1526
/// Note that correctness arguments are similar if you replace `fn` with `FnOnce`.
1527
///
1528
/// What we want this function to do is take a Yokeable (`Y`) that is borrowing from the cart, and
1529
/// produce another Yokeable (`P`) that also borrows from the same cart. There are a couple potential
1530
/// hazards here:
1531
///
1532
/// - `P` ends up borrowing data from `Y` (or elsewhere) that did _not_ come from the cart,
1533
///   for example `P` could borrow owned data from a `Cow`. This would make the `Yoke<P>` dependent
1534
///   on data owned only by the `Yoke<Y>`.
1535
/// - Borrowed data from `Y` escapes with the wrong lifetime
1536
///
1537
/// Let's walk through these and see how they're prevented.
1538
///
1539
/// ```rust, compile_fail
1540
/// # use std::rc::Rc;
1541
/// # use yoke::Yoke;
1542
/// # use std::borrow::Cow;
1543
/// fn borrow_potentially_owned(y: &Yoke<Cow<'static, str>, Rc<[u8]>>) -> Yoke<&'static str, Rc<[u8]>> {
1544
///    y.map_project_cloned(|cow, _| &*cow)
1545
/// }
1546
/// ```
1547
///
1548
/// In this case, the lifetime of `&*cow` is `&'this str`, however the function needs to be able to return
1549
/// `&'a str` _for all `'a`_, which isn't possible.
1550
///
1551
///
1552
/// ```rust, compile_fail
1553
/// # use std::rc::Rc;
1554
/// # use yoke::Yoke;
1555
/// # use std::borrow::Cow;
1556
/// fn borrow_potentially_owned(y: Yoke<Cow<'static, str>, Rc<[u8]>>) -> Yoke<&'static str, Rc<[u8]>> {
1557
///    y.map_project(|cow, _| &*cow)
1558
/// }
1559
/// ```
1560
///
1561
/// This has the same issue, `&*cow` is borrowing for a local lifetime.
1562
///
1563
/// Similarly, trying to project an owned field of a struct will produce similar errors:
1564
///
1565
/// ```rust,compile_fail
1566
/// # use std::borrow::Cow;
1567
/// # use yoke::{Yoke, Yokeable};
1568
/// # use std::mem;
1569
/// # use std::rc::Rc;
1570
/// #
1571
/// // also safely implements Yokeable<'a>
1572
/// struct Bar<'a> {
1573
///     owned: String,
1574
///     string_2: &'a str,
1575
/// }
1576
///
1577
/// fn map_project_owned(bar: &Yoke<Bar<'static>, Rc<[u8]>>) -> Yoke<&'static str, Rc<[u8]>> {
1578
///     // ERROR (but works if you replace owned with string_2)
1579
///     bar.map_project_cloned(|bar, _| &*bar.owned)
1580
/// }
1581
///
1582
/// #
1583
/// # unsafe impl<'a> Yokeable<'a> for Bar<'static> {
1584
/// #     type Output = Bar<'a>;
1585
/// #     fn transform(&'a self) -> &'a Bar<'a> {
1586
/// #         self
1587
/// #     }
1588
/// #
1589
/// #     fn transform_owned(self) -> Bar<'a> {
1590
/// #         // covariant lifetime cast, can be done safely
1591
/// #         self
1592
/// #     }
1593
/// #
1594
/// #     unsafe fn make(from: Bar<'a>) -> Self {
1595
/// #         let ret = mem::transmute_copy(&from);
1596
/// #         mem::forget(from);
1597
/// #         ret
1598
/// #     }
1599
/// #
1600
/// #     fn transform_mut<F>(&'a mut self, f: F)
1601
/// #     where
1602
/// #         F: 'static + FnOnce(&'a mut Self::Output),
1603
/// #     {
1604
/// #         unsafe { f(mem::transmute(self)) }
1605
/// #     }
1606
/// # }
1607
/// ```
1608
///
1609
/// Borrowed data from `Y` similarly cannot escape with the wrong lifetime because of the `for<'a>`, since
1610
/// it will never be valid for the borrowed data to escape for all lifetimes of 'a. Internally, `.map_project()`
1611
/// uses `.get()`, however the signature forces the callers to be able to handle every lifetime.
1612
///
1613
///  `'a` is the only lifetime that matters here; `Yokeable`s must be `'static` and since
1614
/// `Output` is an associated type it can only have one lifetime, `'a` (there's nowhere for it to get another from).
1615
/// `Yoke`s can get additional lifetimes via the cart, and indeed, `map_project()` can operate on `Yoke<_, &'b [u8]>`,
1616
/// however this lifetime is inaccessible to the closure, and even if it were accessible the `for<'a>` would force
1617
/// it out of the output. All external lifetimes (from other found outside the yoke/closures
1618
/// are similarly constrained here.
1619
///
1620
/// Essentially, safety is achieved by using `for<'a> fn(...)` with `'a` used in both `Yokeable`s to ensure that
1621
/// the output yokeable can _only_ have borrowed data flow in to it from the input. All paths of unsoundness require the
1622
/// unification of an existential and universal lifetime, which isn't possible.
1623
const _: () = ();
1624
1625
/// # Safety docs for attach_to_cart()'s signature
1626
///
1627
/// The `attach_to_cart()` family of methods get by by using the following bound:
1628
///
1629
/// ```rust,ignore
1630
/// F: for<'de> FnOnce(&'de <C as Deref>::Target) -> <Y as Yokeable<'de>>::Output,
1631
/// C::Target: 'static
1632
/// ```
1633
///
1634
/// to enforce that the yoking closure produces a yokeable that is *only* allowed to borrow from the cart.
1635
/// A way to be sure of this is as follows: imagine if `F` *did* borrow data of lifetime `'a` and stuff it in
1636
/// its output. Then that lifetime `'a` would have to live at least as long as `'de` *for all `'de`*.
1637
/// The only lifetime that satisfies that is `'static` (since at least one of the potential `'de`s is `'static`),
1638
/// and we're fine with that.
1639
///
1640
/// ## Implied bounds and variance
1641
///
1642
/// The `C::Target: 'static` bound is tricky, however. Let's imagine a situation where we *didn't* have that bound.
1643
///
1644
/// One thing to remember is that we are okay with the cart itself borrowing from places,
1645
/// e.g. `&[u8]` is a valid cart, as is `Box<&[u8]>`. `C` is not `'static`.
1646
///
1647
/// (I'm going to use `CT` in prose to refer to `C::Target` here, since almost everything here has to do
1648
/// with C::Target and not C itself.)
1649
///
1650
/// Unfortunately, there's a sneaky additional bound inside `F`. The signature of `F` is *actually*
1651
///
1652
/// ```rust,ignore
1653
/// F: for<'de> where<C::Target: 'de> FnOnce(&'de C::Target) -> <Y as Yokeable<'de>>::Output
1654
/// ```
1655
///
1656
/// using made-up "where clause inside HRTB" syntax to represent a type that can be represented inside the compiler
1657
/// and type system but not in Rust code. The `CT: 'de` bond comes from the `&'de C::Target`: any time you
1658
/// write `&'a T`, an implied bound of `T: 'a` materializes and is stored alongside it, since references cannot refer
1659
/// to data that itself refers to data of shorter lifetimes. If a reference is valid, its referent must be valid for
1660
/// the duration of the reference's lifetime, so every reference *inside* its referent must also be valid, giving us `T: 'a`.
1661
/// This kind of constraint is often called a "well formedness" constraint: `&'a T` is not "well formed" without that
1662
/// bound, and rustc is being helpful by giving it to us for free.
1663
///
1664
/// Unfortunately, this messes with our universal quantification. The `for<'de>` is no longer "For all lifetimes `'de`",
1665
/// it is "for all lifetimes `'de` *where `CT: 'de`*". And if `CT` borrows from somewhere (with lifetime `'ct`), then we get a
1666
/// `'ct: 'de` bound, and `'de` candidates that live longer than `'ct` won't actually be considered.
1667
/// The neat little logic at the beginning stops working.
1668
///
1669
/// `attach_to_cart()` will instead enforce that the produced yokeable *either* borrows from the cart (fine), or from
1670
/// data that has a lifetime that is at least `'ct`. Which means that `attach_to_cart()` will allow us to borrow locals
1671
/// provided they live at least as long as `'ct`.
1672
///
1673
/// Is this a problem?
1674
///
1675
/// This is totally fine if CT's lifetime is covariant: if C is something like `Box<&'ct [u8]>`, even if our
1676
/// yoked object borrows from locals outliving `'ct`, our Yoke can't outlive that
1677
/// lifetime `'ct` anyway (since it's a part of the cart type), so we're fine.
1678
///
1679
/// However it's completely broken for contravariant carts (e.g. `Box<fn(&'ct u8)>`). In that case
1680
/// we still get `'ct: 'de`, and we still end up being able to
1681
/// borrow from locals that outlive `'ct`. However, our Yoke _can_ outlive
1682
/// that lifetime, because Yoke shares its variance over `'ct`
1683
/// with the cart type, and the cart type is contravariant over `'ct`.
1684
/// So the Yoke can be upcast to having a longer lifetime than `'ct`, and *that* Yoke
1685
/// can outlive `'ct`.
1686
///
1687
/// We fix this by forcing `C::Target: 'static` in `attach_to_cart()`, which would make it work
1688
/// for fewer types, but would also allow Yoke to continue to be covariant over cart lifetimes if necessary.
1689
///
1690
/// An alternate fix would be to not allowing yoke to ever be upcast over lifetimes contained in the cart
1691
/// by forcing them to be invariant. This is a bit more restrictive and affects *all* `Yoke` users, not just
1692
/// those using `attach_to_cart()`.
1693
///
1694
/// See https://github.com/unicode-org/icu4x/issues/2926
1695
/// See also https://github.com/rust-lang/rust/issues/106431 for potentially fixing this upstream by
1696
/// changing how the bound works.
1697
///
1698
/// # Tests
1699
///
1700
/// Here's a broken `attach_to_cart()` that attempts to borrow from a local:
1701
///
1702
/// ```rust,compile_fail
1703
/// use yoke::Yoke;
1704
///
1705
/// let cart = vec![1, 2, 3, 4].into_boxed_slice();
1706
/// let local = vec![4, 5, 6, 7];
1707
/// let yoke: Yoke<&[u8], Box<[u8]>> = Yoke::attach_to_cart(cart, |_| &*local);
1708
/// ```
1709
///
1710
/// Fails as expected.
1711
///
1712
/// And here's a working one with a local borrowed cart that does not do any sneaky borrows whilst attaching.
1713
///
1714
/// ```rust
1715
/// use yoke::Yoke;
1716
///
1717
/// let cart = vec![1, 2, 3, 4].into_boxed_slice();
1718
/// let yoke: Yoke<&[u8], &[u8]> = Yoke::attach_to_cart(&cart, |c| c);
1719
/// ```
1720
///
1721
/// Here's an `attach_to_cart()` that attempts to borrow from a longer-lived local due to
1722
/// the cart being covariant. It fails, but would not if the alternate fix of forcing Yoke to be invariant
1723
/// were implemented. It is technically a safe operation:
1724
///
1725
/// ```rust,compile_fail
1726
/// use yoke::Yoke;
1727
/// // longer lived
1728
/// let local = vec![4, 5, 6, 7];
1729
///
1730
/// let backing = vec![1, 2, 3, 4];
1731
/// let cart = Box::new(&*backing);
1732
///
1733
/// let yoke: Yoke<&[u8], Box<&[u8]>> = Yoke::attach_to_cart(cart, |_| &*local);
1734
/// println!("{:?}", yoke.get());
1735
/// ```
1736
///
1737
/// Finally, here's an `attach_to_cart()` that attempts to borrow from a longer lived local
1738
/// in the case of a contravariant lifetime. It does not compile, but in and of itself is not dangerous:
1739
///
1740
/// ```rust,compile_fail
1741
/// use yoke::Yoke;
1742
///
1743
/// type Contra<'a> = fn(&'a ());
1744
///
1745
/// let local = String::from("Hello World!");
1746
/// let yoke: Yoke<&'static str, Box<Contra<'_>>> = Yoke::attach_to_cart(Box::new((|_| {}) as _), |_| &local[..]);
1747
/// println!("{:?}", yoke.get());
1748
/// ```
1749
///
1750
/// It is dangerous if allowed to transform (testcase from #2926)
1751
///
1752
/// ```rust,compile_fail
1753
/// use yoke::Yoke;
1754
///
1755
/// type Contra<'a> = fn(&'a ());
1756
///
1757
///
1758
/// let local = String::from("Hello World!");
1759
/// let yoke: Yoke<&'static str, Box<Contra<'_>>> = Yoke::attach_to_cart(Box::new((|_| {}) as _), |_| &local[..]);
1760
/// println!("{:?}", yoke.get());
1761
/// let yoke_longer: Yoke<&'static str, Box<Contra<'static>>> = yoke;
1762
/// let leaked: &'static Yoke<&'static str, Box<Contra<'static>>> = Box::leak(Box::new(yoke_longer));
1763
/// let reference: &'static str = leaked.get();
1764
///
1765
/// println!("pre-drop: {reference}");
1766
/// drop(local);
1767
/// println!("post-drop: {reference}");
1768
/// ```
1769
const _: () = ();
1770
1771
/// # Safety docs for *map_with_cart*()
1772
///
1773
/// [`Yoke::map_with_cart`] has both the problems of [`Yoke::map_project`] (with a
1774
/// potentially-pathological callback) and [`Yoke::attach_to_cart`] (with a potentially
1775
/// pathological cart, capable of permitting a bad callback).
1776
///
1777
/// [`map_project`] forces the callback to be well-behaved with the bounds:
1778
/// ```rust,ignore
1779
/// F: for<'a> FnOnce(
1780
///     <Y as Yokeable<'a>>::Output,
1781
///     PhantomData<&'a ()>,
1782
/// ) -> <P as Yokeable<'a>>::Output,
1783
/// ```
1784
///
1785
/// The `for<'a>` constraint prevents `F` from inserting additional borrows that did not come
1786
/// from the input; `<P as Yokeable<'a>>::Output` can be `'static` or only `'a`, but that
1787
/// `'a` could potentially be `'static` as well. Therefore, `F` has to be capable of returning
1788
/// `'static` data (under certain constraints), and cannot insert additional borrows. Nor can a
1789
/// reference leak out, as for a sufficiently short `'a`, the data would not live long enough.
1790
/// The `PhantomData<&'a ()>` is just to make sure that the lifetime `'a` is constrained
1791
/// to fix <https://github.com/rust-lang/rust/issues/86702>.
1792
///
1793
/// Next, [`Yoke::attach_to_cart`] follows mostly the same approach, but needs to ensure that
1794
/// the `for<'a>` bound remains a fully universal quantifier.
1795
/// It uses the bounds:
1796
/// ```rust,ignore
1797
/// F: for<'de> FnOnce(&'de <C as Deref>::Target) -> <Y as Yokeable<'de>>::Output,
1798
/// <C as Deref>::Target: 'static,
1799
/// ```
1800
///
1801
/// The problem is that the `for<'de>` quantifier is bounded by whatever the lifetime of
1802
/// `<C as Deref>::Target` is, so for it to cover all lifetimes, `<C as Deref>::Target` must
1803
/// be `'static`.
1804
///
1805
///
1806
/// [`Yoke::map_with_cart`] combines the relevant bounds into one:
1807
/// ```rust,ignore
1808
/// F: for<'a> FnOnce(
1809
///     <Y as Yokeable<'a>>::Output,
1810
///     &'a <C as Deref>::Target,
1811
/// ) -> <P as Yokeable<'a>>::Output,
1812
/// <C as Deref>::Target: 'static,
1813
/// ```
1814
///
1815
/// The techniques ensure that, for any lifetime `'a`, the callback must be capable of taking in
1816
/// data from the old `Yokeable` and from the cart which is known only to outlive `'a`, and return
1817
/// data that outlives `'a`. `F` is incapable of inserting external data which is not `'static`,
1818
/// and is otherwise constrained to using the data in the cart and old `Yokeable` to produce
1819
/// a new `Yokeable`.
1820
/// A `PhantomData` is not needed, since the lifetime `'a` is constrained by
1821
/// `&'a <C as Deref>::Target`.
1822
///
1823
/// # Fail tests
1824
///
1825
/// We can confirm that problematic cases analogous to those in [`Yoke::map_project`] and
1826
/// [`Yoke::attach_to_cart`] still fail here. They're copied and adapted slightly.
1827
///
1828
/// ### From `map_project`'s safety docs
1829
///
1830
/// ```rust, compile_fail
1831
/// # use std::rc::Rc;
1832
/// # use yoke::Yoke;
1833
/// # use std::borrow::Cow;
1834
/// fn borrow_potentially_owned(y: &Yoke<Cow<'static, str>, Rc<[u8]>>) -> Yoke<&'static str, Rc<[u8]>> {
1835
///    y.map_with_cart_cloned(|cow, _cart| &**cow)
1836
/// }
1837
/// ```
1838
///
1839
/// ```rust, compile_fail
1840
/// # use std::rc::Rc;
1841
/// # use yoke::Yoke;
1842
/// # use std::borrow::Cow;
1843
/// fn borrow_potentially_owned(y: Yoke<Cow<'static, str>, Rc<[u8]>>) -> Yoke<&'static str, Rc<[u8]>> {
1844
///    y.map_with_cart(|cow: Cow<'_, _>, _cart| &*cow)
1845
/// }
1846
/// ```
1847
///
1848
/// ```rust,compile_fail
1849
/// # use std::borrow::Cow;
1850
/// # use yoke::{Yoke, Yokeable};
1851
/// # use std::mem;
1852
/// # use std::rc::Rc;
1853
/// #
1854
/// // also safely implements Yokeable<'a>
1855
/// struct Bar<'a> {
1856
///     owned: String,
1857
///     string_2: &'a str,
1858
/// }
1859
///
1860
/// fn map_with_cart_owned(bar: &Yoke<Bar<'static>, Rc<[u8]>>) -> Yoke<&'static str, Rc<[u8]>> {
1861
///     // ERROR (but works if you replace owned with string_2)
1862
///     bar.map_with_cart_cloned(|bar, _cart| &*bar.owned)
1863
/// }
1864
///
1865
/// #
1866
/// # unsafe impl<'a> Yokeable<'a> for Bar<'static> {
1867
/// #     type Output = Bar<'a>;
1868
/// #     fn transform(&'a self) -> &'a Bar<'a> {
1869
/// #         self
1870
/// #     }
1871
/// #
1872
/// #     fn transform_owned(self) -> Bar<'a> {
1873
/// #         // covariant lifetime cast, can be done safely
1874
/// #         self
1875
/// #     }
1876
/// #
1877
/// #     unsafe fn make(from: Bar<'a>) -> Self {
1878
/// #         let ret = mem::transmute_copy(&from);
1879
/// #         mem::forget(from);
1880
/// #         ret
1881
/// #     }
1882
/// #
1883
/// #     fn transform_mut<F>(&'a mut self, f: F)
1884
/// #     where
1885
/// #         F: 'static + FnOnce(&'a mut Self::Output),
1886
/// #     {
1887
/// #         unsafe { f(mem::transmute(self)) }
1888
/// #     }
1889
/// # }
1890
/// ```
1891
///
1892
/// ### From `attach_to_cart`'s safety docs
1893
///
1894
/// Being slightly paranoid, confirm that the expected line is the one causing the error.
1895
/// ```rust
1896
/// use std::rc::Rc;
1897
/// use yoke::Yoke;
1898
///
1899
/// let cart: Vec<u8> = vec![1, 2, 3, 4];
1900
/// let cart: Rc<[u8]> = Rc::from(&*cart);
1901
///
1902
/// let local = vec![4, 5, 6, 7];
1903
/// let local: Rc<[u8]> = Rc::from(&*local);
1904
///
1905
/// let yoke: Yoke<&[u8], Rc<[u8]>> = Yoke::attach_to_cart(cart, |cart| cart);
1906
/// let yoke: Yoke<&[u8], Rc<[u8]>> = yoke.map_with_cart(|_, cart| cart);
1907
/// ```
1908
///
1909
/// ```rust,compile_fail
1910
/// use std::rc::Rc;
1911
/// use yoke::Yoke;
1912
///
1913
/// let cart: Vec<u8> = vec![1, 2, 3, 4];
1914
/// let cart: Rc<[u8]> = Rc::from(&*cart);
1915
///
1916
/// let local = vec![4, 5, 6, 7];
1917
/// let local: Rc<[u8]> = Rc::from(&*local);
1918
///
1919
/// let yoke: Yoke<&[u8], Rc<[u8]>> = Yoke::attach_to_cart(cart, |cart| &*cart);
1920
/// let yoke: Yoke<&[u8], Rc<[u8]>> = yoke.map_with_cart(|_, _| &*local);
1921
/// ```
1922
///
1923
///
1924
/// ```rust
1925
/// use std::rc::Rc;
1926
/// use yoke::Yoke;
1927
///
1928
/// // longer lived
1929
/// let local = vec![4_u8, 5, 6, 7];
1930
/// let local: Rc<[u8]> = Rc::from(&*local);
1931
///
1932
/// let backing = vec![1_u8, 2, 3, 4];
1933
/// let cart: Rc<[u8]> = Rc::from(&*backing);
1934
///
1935
/// let yoke: Yoke<&[u8], Rc<[u8]>> = Yoke::attach_to_cart(cart, |cart| cart);
1936
/// let yoke: Yoke<&[u8], Rc<[u8]>> = yoke.map_with_cart(|_, cart: &[u8]| cart);
1937
/// println!("{:?}", yoke.get());
1938
/// ```
1939
///
1940
/// ```rust,compile_fail
1941
/// use std::rc::Rc;
1942
/// use yoke::Yoke;
1943
///
1944
/// // longer lived
1945
/// let local: Rc<[u8]> = Rc::from(&*local);
1946
///
1947
/// let backing = vec![1_u8, 2, 3, 4];
1948
/// let cart: Rc<[u8]> = Rc::from(&*backing);
1949
///
1950
/// let yoke: Yoke<&[u8], Rc<[u8]>> = Yoke::attach_to_cart(cart, |cart| &*cart);
1951
/// let yoke: Yoke<&[u8], Rc<[u8]>> = yoke.map_with_cart(|_, cart: &[u8]| &*local);
1952
/// println!("{:?}", yoke.get());
1953
/// ```
1954
///
1955
///
1956
/// I don't see a way to closely adapt `attach_to_cart`'s last two test cases on contravariant
1957
/// carts, since the problematic `Cart` type is stopped at the stage of construction. We can use
1958
/// one of `Yoke`'s other constructors instead, and try mapping it.
1959
///
1960
/// ```rust
1961
/// use std::rc::Rc;
1962
/// use yoke::Yoke;
1963
///
1964
/// type Contra<'a> = fn(&'a ());
1965
///
1966
/// let local = String::from("Hello World!");
1967
/// let yoke: Yoke<&'static str, Option<Rc<Contra<'_>>>> =
1968
///     Yoke::new_owned("hi");
1969
/// println!("{:?}", yoke.get());
1970
/// ```
1971
///
1972
/// This case might actually be fine to allow, since `attach_to_cart` could not possibly succeed
1973
/// with this cart type and thus the `Yokeable` must always be owned. But whether it's safe to
1974
/// permit *any* contravariant cart in `map_with_cart` is not immediately clear to me. Therefore,
1975
/// compile fail.
1976
/// ```rust,compile_fail
1977
/// use std::rc::Rc;
1978
/// use yoke::Yoke;
1979
///
1980
/// type Contra<'a> = fn(&'a ());
1981
///
1982
/// fn scope<'b>() {
1983
///     let local = String::from("Hello World!");
1984
///     let yoke: Yoke<&'static str, Option<Rc<Contra<'b>>>> = Yoke::new_owned("hi");
1985
///     let yoke: Yoke<&'static str, Rc<Option<Rc<Contra<'b>>>>> = yoke.wrap_cart_in_rc);
1986
///     let yoke: Yoke<&'static str, Rc<Option<Rc<Contra<'b>>>>> = yoke.map_with_cart(|yoke, _| yoke);
1987
///     println!("{:?}", yoke.get());
1988
/// }
1989
/// ```
1990
///
1991
/// This version succeeds, though.
1992
/// ```rust
1993
/// use std::rc::Rc;
1994
/// use yoke::Yoke;
1995
///
1996
/// type Contra<'a> = fn(&'a ());
1997
///
1998
/// fn scope<'b>() {
1999
///     let local = String::from("Hello World!");
2000
///     let yoke: Yoke<&'static str, Option<Rc<Contra<'b>>>> =
2001
///         Yoke::new_owned("hi");
2002
///     let yoke: Yoke<&'static str, Rc<Option<Rc<Contra<'b>>>>> =
2003
///         yoke.wrap_cart_in_rc();
2004
///     let yoke: Yoke<&'static str, Rc<Option<Rc<Contra<'static>>>>> = yoke;
2005
///     let yoke: Yoke<&'static str, Rc<Option<Rc<Contra<'static>>>>> =
2006
///         yoke.map_with_cart(|yoke, _| yoke);
2007
///     println!("{:?}", yoke.get());
2008
/// }
2009
/// ```
2010
///
2011
/// # Test running the function
2012
///
2013
/// The above verifies the method signature. We can also check that the implementation is correct,
2014
/// by running Miri on the following test (analogous to [`Yoke::map_with_cart`]'s doctest):
2015
/// ```
2016
/// use std::rc::Rc;
2017
/// use yoke::Yoke;
2018
///
2019
/// type Foo<'a> = Option<&'a str>;
2020
/// type Bar<'a> = (Option<&'a str>, Option<&'a str>);
2021
///
2022
/// fn foo_to_bar(
2023
///     foo: Yoke<Foo<'static>, Rc<str>>,
2024
/// ) -> Yoke<Bar<'static>, Rc<str>> {
2025
///     foo.map_with_cart(|foo, cart| (foo, cart.lines().next_back()))
2026
/// }
2027
///
2028
/// fn foo_to_bar_cloned(
2029
///     foo: &Yoke<Foo<'static>, Rc<str>>,
2030
/// ) -> Yoke<Bar<'static>, Rc<str>> {
2031
///     foo.map_with_cart_cloned(|foo, cart| (*foo, cart.lines().next_back()))
2032
/// }
2033
///
2034
/// fn bar_to_foo(
2035
///     bar: Yoke<Bar<'static>, Rc<str>>,
2036
/// ) -> Yoke<Foo<'static>, Rc<str>> {
2037
///     bar.map_project(|bar, _| (bar.0))
2038
/// }
2039
///
2040
/// fn main() {
2041
///     fn assert_hello_world(bar: &Yoke<Bar<'static>, Rc<str>>) {
2042
///         assert_eq!(bar.get().0, Some("hello"));
2043
///         assert_eq!(bar.get().1, Some("world"));
2044
///     }
2045
///
2046
///     let foo = Yoke::<Foo<'static>, Rc<str>>::attach_to_cart(
2047
///         Rc::from("hello\nworld"),
2048
///         |cart| cart.lines().next(),
2049
///     );
2050
///
2051
///     assert_eq!(*foo.get(), Some("hello"));
2052
///
2053
///     let bar = foo_to_bar(foo);
2054
///     assert_hello_world(&bar);
2055
///
2056
///     let foo = bar_to_foo(bar);
2057
///
2058
///     let bar_one = foo_to_bar_cloned(&foo);
2059
///     let bar_two = foo_to_bar_cloned(&foo);
2060
///
2061
///     assert_hello_world(&bar_one);
2062
///     assert_hello_world(&bar_two);
2063
/// }
2064
/// ```
2065
const _: () = ();