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::utils;
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 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
97impl<Y: for<'a> Yokeable<'a>, C: core::fmt::Debug> core::fmt::Debug for Yoke<Y, C>
98where
99 for<'a> <Y as Yokeable<'a>>::Output: core::fmt::Debug,
100{
101 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
102 f.debug_struct("Yoke")
103 .field("yokeable", self.get())
104 .field("cart", self.backing_cart())
105 .finish()
106 }
107}
108
109#[test]
110fn 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
121impl<Y: for<'a> Yokeable<'a>, C: StableDeref> Yoke<Y, C>
122where
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 pub fn attach_to_cart<F>(cart: C, f: F) -> Self
194 where
195 // safety note: This works by enforcing that the *only* place the return value of F
196 // can borrow from is the cart, since `F` must be valid for all lifetimes `'de`
197 //
198 // The <C as Deref>::Target: 'static on the impl is crucial for safety as well
199 //
200 // See safety docs at the bottom of this file for more information
201 F: for<'de> FnOnce(&'de <C as Deref>::Target) -> <Y as Yokeable<'de>>::Output,
202 <C as Deref>::Target: 'static,
203 {
204 let deserialized = f(cart.deref());
205 Self {
206 yokeable: KindaSortaDangling::new(
207 // Safety: the resulting `yokeable` is dropped before the `cart` because
208 // of the Yoke invariant. See the safety docs at the bottom of this file
209 // for the justification of why yokeable could only borrow from the Cart.
210 unsafe { Y::make(deserialized) },
211 ),
212 cart,
213 }
214 }
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 pub fn try_attach_to_cart<E, F>(cart: C, f: F) -> Result<Self, E>
222 where
223 F: for<'de> FnOnce(&'de <C as Deref>::Target) -> Result<<Y as Yokeable<'de>>::Output, E>,
224 <C as Deref>::Target: 'static,
225 {
226 let deserialized = f(cart.deref())?;
227 Ok(Self {
228 yokeable: KindaSortaDangling::new(
229 // Safety: the resulting `yokeable` is dropped before the `cart` because
230 // of the Yoke invariant. See the safety docs at the bottom of this file
231 // for the justification of why yokeable could only borrow from the Cart.
232 unsafe { Y::make(deserialized) },
233 ),
234 cart,
235 })
236 }
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 pub fn attach_to_cart_badly(
243 cart: C,
244 f: for<'de> fn(&'de <C as Deref>::Target) -> <Y as Yokeable<'de>>::Output,
245 ) -> Self {
246 Self::attach_to_cart(cart, f)
247 }
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 pub fn try_attach_to_cart_badly<E>(
254 cart: C,
255 f: for<'de> fn(&'de <C as Deref>::Target) -> Result<<Y as Yokeable<'de>>::Output, E>,
256 ) -> Result<Self, E> {
257 Self::try_attach_to_cart(cart, f)
258 }
259}
260
261impl<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 pub fn get<'a>(&'a self) -> &'a <Y as Yokeable<'a>>::Output {
293 self.yokeable.transform()
294 }
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 pub fn backing_cart(&self) -> &C {
301 &self.cart
302 }
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 pub fn into_backing_cart(self) -> C {
354 self.cart
355 }
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 pub unsafe fn replace_cart<C2>(self, f: impl FnOnce(C) -> C2) -> Yoke<Y, C2> {
384 Yoke {
385 // Safety note: the safety invariant of this function guarantees that
386 // the data that the yokeable references has its ownership (if any)
387 // transferred to the new cart before self.cart is dropped.
388 yokeable: self.yokeable,
389 cart: f(self.cart),
390 }
391 }
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 pub fn with_mut<'a, F>(&'a mut self, f: F)
456 where
457 F: 'static + for<'b> FnOnce(&'b mut <Y as Yokeable<'a>>::Output),
458 {
459 self.yokeable.transform_mut(f);
460 }
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 pub fn with_mut_return<'a, F, R>(&'a mut self, f: F) -> R
472 where
473 F: 'static + for<'b> FnOnce(&'b mut <Y as Yokeable<'a>>::Output) -> R,
474 R: 'static,
475 {
476 utils::transform_mut_yokeable(&mut *self.yokeable, f)
477 }
478
479 /// Helper function allowing one to wrap the cart type `C` in an `Option<T>`.
480 #[inline]
481 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 unsafe { self.replace_cart(Some) }
485 }
486}
487
488impl<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 pub fn new_always_owned(yokeable: Y) -> Self {
511 Self {
512 // Safety note: this `yokeable` certainly does not reference data owned by (), so we do
513 // not have to worry about when the `yokeable` is dropped.
514 yokeable: KindaSortaDangling::new(yokeable),
515 cart: (),
516 }
517 }
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 pub fn into_yokeable(self) -> Y {
525 // Safety note: since `yokeable` cannot reference data owned by `()`, this is certainly
526 // safe.
527 self.yokeable.into_inner()
528 }
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,
533impl<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 pub const fn new_owned(yokeable: Y) -> Self {
559 Self {
560 // Safety note: this `yokeable` is known not to borrow from the cart.
561 yokeable: KindaSortaDangling::new(yokeable),
562 cart: None,
563 }
564 }
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 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 match self.cart {
574 Some(_) => Err(self),
575 None => Ok(self.yokeable.into_inner()),
576 }
577 }
578}
579
580impl<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 pub fn convert_cart_into_option_pointer(self) -> Yoke<Y, CartableOptionPointer<C>> {
630 match self.cart {
631 Some(cart) => Yoke {
632 // Safety note: CartableOptionPointer::from_cartable only wraps the `cart`,
633 // so the data referenced by the yokeable is still live.
634 yokeable: self.yokeable,
635 cart: CartableOptionPointer::from_cartable(cart),
636 },
637 None => Yoke {
638 // Safety note: this Yokeable cannot refer to any data since self.cart is None.
639 yokeable: self.yokeable,
640 cart: CartableOptionPointer::none(),
641 },
642 }
643 }
644}
645
646impl<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 pub fn try_into_yokeable(self) -> Result<Y, Self> {
653 if self.cart.is_none() {
654 Ok(self.yokeable.into_inner())
655 } else {
656 Err(self)
657 }
658 }
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.
676pub unsafe trait CloneableCart: Clone {}
677
678#[cfg(feature = "alloc")]
679// Safety: Rc<T> implements CloneStableDeref.
680unsafe impl<T: ?Sized> CloneableCart for Rc<T> {}
681#[cfg(feature = "alloc")]
682// Safety: Arc<T> implements CloneStableDeref.
683unsafe impl<T: ?Sized> CloneableCart for Arc<T> {}
684// Safety: Option<T> cannot deref to anything that T doesn't already deref to.
685unsafe 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.
688unsafe impl<'a, T: ?Sized> CloneableCart for &'a T {}
689// Safety: () cannot deref to anything.
690unsafe 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.
701impl<Y: for<'a> Yokeable<'a>, C: CloneableCart> Clone for Yoke<Y, C>
702where
703 for<'a> <Y as Yokeable<'a>>::Output: Clone,
704{
705 fn clone(&self) -> Self {
706 // We have an &T not a T, and we can clone T
707 let this = self.get().clone();
708 Yoke {
709 yokeable: KindaSortaDangling::new(
710 // Safety: C being a CloneableCart guarantees that the data referenced by the
711 // `yokeable` is kept alive by the clone of the cart.
712 unsafe { Y::make(this) },
713 ),
714 cart: self.cart.clone(),
715 }
716 }
717}
718
719#[test]
720fn 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
751impl<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 pub fn map_project<P, F>(self, f: F) -> Yoke<P, C>
820 where
821 P: for<'a> Yokeable<'a>,
822 F: for<'a> FnOnce(
823 <Y as Yokeable<'a>>::Output,
824 PhantomData<&'a ()>,
825 ) -> <P as Yokeable<'a>>::Output,
826 {
827 let p = f(self.yokeable.into_inner().transform_owned(), PhantomData);
828 Yoke {
829 yokeable: KindaSortaDangling::new(
830 // Safety: the resulting `yokeable` is dropped before the `cart` because
831 // of the Yoke invariant. See the safety docs below for the justification of why
832 // yokeable could only borrow from the Cart.
833 unsafe { P::make(p) },
834 ),
835 cart: self.cart,
836 }
837 }
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 pub fn map_project_cloned<'this, P, F>(&'this self, f: F) -> Yoke<P, C>
845 where
846 P: for<'a> Yokeable<'a>,
847 C: CloneableCart,
848 F: for<'a> FnOnce(
849 &'this <Y as Yokeable<'a>>::Output,
850 PhantomData<&'a ()>,
851 ) -> <P as Yokeable<'a>>::Output,
852 {
853 let p = f(self.get(), PhantomData);
854 Yoke {
855 yokeable: KindaSortaDangling::new(
856 // Safety: the resulting `yokeable` is dropped before the `cart` because
857 // of the Yoke invariant. See the safety docs below for the justification of why
858 // yokeable could only borrow from the Cart.
859 unsafe { P::make(p) },
860 ),
861 cart: self.cart.clone(),
862 }
863 }
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 pub fn try_map_project<P, F, E>(self, f: F) -> Result<Yoke<P, C>, E>
925 where
926 P: for<'a> Yokeable<'a>,
927 F: for<'a> FnOnce(
928 <Y as Yokeable<'a>>::Output,
929 PhantomData<&'a ()>,
930 ) -> Result<<P as Yokeable<'a>>::Output, E>,
931 {
932 let p = f(self.yokeable.into_inner().transform_owned(), PhantomData)?;
933 Ok(Yoke {
934 yokeable: KindaSortaDangling::new(
935 // Safety: the resulting `yokeable` is dropped before the `cart` because
936 // of the Yoke invariant. See the safety docs below for the justification of why
937 // yokeable could only borrow from the Cart.
938 unsafe { P::make(p) },
939 ),
940 cart: self.cart,
941 })
942 }
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 pub fn try_map_project_cloned<'this, P, F, E>(&'this self, f: F) -> Result<Yoke<P, C>, E>
950 where
951 P: for<'a> Yokeable<'a>,
952 C: CloneableCart,
953 F: for<'a> FnOnce(
954 &'this <Y as Yokeable<'a>>::Output,
955 PhantomData<&'a ()>,
956 ) -> Result<<P as Yokeable<'a>>::Output, E>,
957 {
958 let p = f(self.get(), PhantomData)?;
959 Ok(Yoke {
960 yokeable: KindaSortaDangling::new(
961 // Safety: the resulting `yokeable` is dropped before the `cart` because
962 // of the Yoke invariant. See the safety docs below for the justification of why
963 // yokeable could only borrow from the Cart.
964 unsafe { P::make(p) },
965 ),
966 cart: self.cart.clone(),
967 })
968 }
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 pub fn map_project_with_explicit_capture<P, T>(
975 self,
976 capture: T,
977 f: for<'a> fn(
978 <Y as Yokeable<'a>>::Output,
979 capture: T,
980 PhantomData<&'a ()>,
981 ) -> <P as Yokeable<'a>>::Output,
982 ) -> Yoke<P, C>
983 where
984 P: for<'a> Yokeable<'a>,
985 {
986 let p = f(
987 self.yokeable.into_inner().transform_owned(),
988 capture,
989 PhantomData,
990 );
991 Yoke {
992 yokeable: KindaSortaDangling::new(
993 // Safety: the resulting `yokeable` is dropped before the `cart` because
994 // of the Yoke invariant. See the safety docs below for the justification of why
995 // yokeable could only borrow from the Cart.
996 unsafe { P::make(p) },
997 ),
998 cart: self.cart,
999 }
1000 }
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 pub fn map_project_cloned_with_explicit_capture<'this, P, T>(
1008 &'this self,
1009 capture: T,
1010 f: for<'a> fn(
1011 &'this <Y as Yokeable<'a>>::Output,
1012 capture: T,
1013 PhantomData<&'a ()>,
1014 ) -> <P as Yokeable<'a>>::Output,
1015 ) -> Yoke<P, C>
1016 where
1017 P: for<'a> Yokeable<'a>,
1018 C: CloneableCart,
1019 {
1020 let p = f(self.get(), capture, PhantomData);
1021 Yoke {
1022 yokeable: KindaSortaDangling::new(
1023 // Safety: the resulting `yokeable` is dropped before the `cart` because
1024 // of the Yoke invariant. See the safety docs below for the justification of why
1025 // yokeable could only borrow from the Cart.
1026 unsafe { P::make(p) },
1027 ),
1028 cart: self.cart.clone(),
1029 }
1030 }
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 pub fn try_map_project_with_explicit_capture<P, T, E>(
1039 self,
1040 capture: T,
1041 f: for<'a> fn(
1042 <Y as Yokeable<'a>>::Output,
1043 capture: T,
1044 PhantomData<&'a ()>,
1045 ) -> Result<<P as Yokeable<'a>>::Output, E>,
1046 ) -> Result<Yoke<P, C>, E>
1047 where
1048 P: for<'a> Yokeable<'a>,
1049 {
1050 let p = f(
1051 self.yokeable.into_inner().transform_owned(),
1052 capture,
1053 PhantomData,
1054 )?;
1055 Ok(Yoke {
1056 yokeable: KindaSortaDangling::new(
1057 // Safety: the resulting `yokeable` is dropped before the `cart` because
1058 // of the Yoke invariant. See the safety docs below for the justification of why
1059 // yokeable could only borrow from the Cart.
1060 unsafe { P::make(p) },
1061 ),
1062 cart: self.cart,
1063 })
1064 }
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 pub fn try_map_project_cloned_with_explicit_capture<'this, P, T, E>(
1073 &'this self,
1074 capture: T,
1075 f: for<'a> fn(
1076 &'this <Y as Yokeable<'a>>::Output,
1077 capture: T,
1078 PhantomData<&'a ()>,
1079 ) -> Result<<P as Yokeable<'a>>::Output, E>,
1080 ) -> Result<Yoke<P, C>, E>
1081 where
1082 P: for<'a> Yokeable<'a>,
1083 C: CloneableCart,
1084 {
1085 let p = f(self.get(), capture, PhantomData)?;
1086 Ok(Yoke {
1087 yokeable: KindaSortaDangling::new(
1088 // Safety: the resulting `yokeable` is dropped before the `cart` because
1089 // of the Yoke invariant. See the safety docs below for the justification of why
1090 // yokeable could only borrow from the Cart.
1091 unsafe { P::make(p) },
1092 ),
1093 cart: self.cart.clone(),
1094 })
1095 }
1096}
1097
1098impl<Y: for<'a> Yokeable<'a>, C: StableDeref> Yoke<Y, C>
1099where
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 pub fn map_with_cart<P, F>(self, f: F) -> Yoke<P, C>
1199 where
1200 P: for<'a> Yokeable<'a>,
1201 F: for<'a> FnOnce(
1202 <Y as Yokeable<'a>>::Output,
1203 &'a <C as Deref>::Target,
1204 ) -> <P as Yokeable<'a>>::Output,
1205 <C as Deref>::Target: 'static,
1206 {
1207 let p = f(
1208 self.yokeable.into_inner().transform_owned(),
1209 self.cart.deref(),
1210 );
1211 Yoke {
1212 yokeable: KindaSortaDangling::new(
1213 // Safety: the resulting `yokeable` is dropped before the `cart` because
1214 // of the Yoke invariant. See the safety docs below for the justification of why
1215 // yokeable could only borrow from the Cart.
1216 unsafe { P::make(p) },
1217 ),
1218 cart: self.cart,
1219 }
1220 }
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 pub fn map_with_cart_cloned<'this, P, F>(&'this self, f: F) -> Yoke<P, C>
1232 where
1233 P: for<'a> Yokeable<'a>,
1234 F: for<'a> FnOnce(
1235 &'this <Y as Yokeable<'a>>::Output,
1236 &'a <C as Deref>::Target,
1237 ) -> <P as Yokeable<'a>>::Output,
1238 C: CloneableCart,
1239 <C as Deref>::Target: 'static,
1240 {
1241 let p = f(self.get(), self.cart.deref());
1242 Yoke {
1243 yokeable: KindaSortaDangling::new(
1244 // Safety: the resulting `yokeable` is dropped before the `cart` because
1245 // of the Yoke invariant. See the safety docs below for the justification of why
1246 // yokeable could only borrow from the Cart.
1247 unsafe { P::make(p) },
1248 ),
1249 cart: self.cart.clone(),
1250 }
1251 }
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 pub fn try_map_with_cart<P, F, E>(self, f: F) -> Result<Yoke<P, C>, E>
1277 where
1278 P: for<'a> Yokeable<'a>,
1279 F: for<'a> FnOnce(
1280 <Y as Yokeable<'a>>::Output,
1281 &'a <C as Deref>::Target,
1282 ) -> Result<<P as Yokeable<'a>>::Output, E>,
1283 <C as Deref>::Target: 'static,
1284 {
1285 let p = f(
1286 self.yokeable.into_inner().transform_owned(),
1287 self.cart.deref(),
1288 )?;
1289 Ok(Yoke {
1290 yokeable: KindaSortaDangling::new(
1291 // Safety: the resulting `yokeable` is dropped before the `cart` because
1292 // of the Yoke invariant. See the safety docs below for the justification of why
1293 // yokeable could only borrow from the Cart.
1294 unsafe { P::make(p) },
1295 ),
1296 cart: self.cart,
1297 })
1298 }
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 pub fn try_map_with_cart_cloned<'this, P, F, E>(&'this self, f: F) -> Result<Yoke<P, C>, E>
1310 where
1311 P: for<'a> Yokeable<'a>,
1312 C: CloneableCart,
1313 F: for<'a> FnOnce(
1314 &'this <Y as Yokeable<'a>>::Output,
1315 &'a <C as Deref>::Target,
1316 ) -> Result<<P as Yokeable<'a>>::Output, E>,
1317 <C as Deref>::Target: 'static,
1318 {
1319 let p = f(self.get(), self.cart.deref())?;
1320 Ok(Yoke {
1321 yokeable: KindaSortaDangling::new(
1322 // Safety: the resulting `yokeable` is dropped before the `cart` because
1323 // of the Yoke invariant. See the safety docs below for the justification of why
1324 // yokeable could only borrow from the Cart.
1325 unsafe { P::make(p) },
1326 ),
1327 cart: self.cart.clone(),
1328 })
1329 }
1330}
1331
1332#[cfg(feature = "alloc")]
1333impl<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")]
1376impl<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")]
1419impl<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")]
1462impl<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
1494impl<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 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 unsafe { self.replace_cart(EitherCart::A) }
1505 }
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 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 unsafe { self.replace_cart(EitherCart::B) }
1516 }
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.
1623const _: () = ();
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/// ```
1769const _: () = ();
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/// ```
2065const _: () = ();