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