yoke/
yoke.rs

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