| { | ||
| "git": { | ||
| "sha1": "7a471b7e4584bfc165d9e593295742c880369bf7" | ||
| "sha1": "196823aa3a859324cca61b61e7aafa4c98988f1a" | ||
| }, | ||
| "path_in_vcs": "utils/yoke" | ||
| } |
+8
-5
@@ -15,3 +15,3 @@ # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO | ||
| name = "yoke" | ||
| version = "0.6.2" | ||
| version = "0.7.0" | ||
| authors = ["Manish Goregaokar <manishsmail@gmail.com>"] | ||
@@ -44,2 +44,5 @@ include = [ | ||
| [package.metadata.workspaces] | ||
| independent = true | ||
| [package.metadata.docs.rs] | ||
@@ -58,7 +61,7 @@ all-features = true | ||
| [dependencies.yoke-derive] | ||
| version = "0.6.0" | ||
| version = "0.7.0" | ||
| optional = true | ||
| [dependencies.zerofrom] | ||
| version = "0.1.0" | ||
| version = "0.1.1" | ||
| optional = true | ||
@@ -76,3 +79,3 @@ default-features = false | ||
| "stable_deref_trait/alloc", | ||
| "serde/alloc", | ||
| "serde?/alloc", | ||
| "zerofrom/alloc", | ||
@@ -85,4 +88,4 @@ ] | ||
| derive = [ | ||
| "yoke-derive", | ||
| "dep:yoke-derive", | ||
| "zerofrom/derive", | ||
| ] |
+4
-4
@@ -10,3 +10,3 @@ // This file is part of ICU4X. For terms of use, please see the file | ||
| //! | ||
| //! Available with the `"alloc"` feature enabled. | ||
| //! Available with the `"alloc"` Cargo feature enabled. | ||
@@ -29,3 +29,3 @@ use alloc::boxed::Box; | ||
| /// | ||
| /// Available with the `"alloc"` feature enabled. | ||
| /// Available with the `"alloc"` Cargo feature enabled. | ||
| pub type ErasedArcCart = Arc<dyn ErasedDestructor + Send + Sync>; | ||
@@ -36,3 +36,3 @@ /// A type-erased Cart that has `Rc` semantics | ||
| /// | ||
| /// Available with the `"alloc"` feature enabled. | ||
| /// Available with the `"alloc"` Cargo feature enabled. | ||
| pub type ErasedRcCart = Rc<dyn ErasedDestructor>; | ||
@@ -43,3 +43,3 @@ /// A type-erased Cart that has `Box` semantics | ||
| /// | ||
| /// Available with the `"alloc"` feature enabled. | ||
| /// Available with the `"alloc"` Cargo feature enabled. | ||
| pub type ErasedBoxCart = Box<dyn ErasedDestructor>; |
+0
-2
@@ -51,3 +51,2 @@ // This file is part of ICU4X. For terms of use, please see the file | ||
| pub mod erased; | ||
| mod is_covariant; | ||
| mod macro_impls; | ||
@@ -63,3 +62,2 @@ pub mod trait_hack; | ||
| pub use crate::is_covariant::IsCovariant; | ||
| pub use crate::yoke::{CloneableCart, Yoke}; | ||
@@ -66,0 +64,0 @@ pub use crate::yokeable::Yokeable; |
@@ -9,3 +9,3 @@ // This file is part of ICU4X. For terms of use, please see the file | ||
| use crate::{IsCovariant, Yokeable}; | ||
| use crate::Yokeable; | ||
| use core::{mem, ptr}; | ||
@@ -42,3 +42,2 @@ | ||
| } | ||
| unsafe impl<'a> IsCovariant<'a> for $ty {} | ||
| }; | ||
@@ -45,0 +44,0 @@ } |
+200
-28
@@ -9,3 +9,2 @@ // This file is part of ICU4X. For terms of use, please see the file | ||
| use crate::trait_hack::YokeTraitHack; | ||
| use crate::IsCovariant; | ||
| use crate::Yokeable; | ||
@@ -52,4 +51,3 @@ use core::marker::PhantomData; | ||
| /// | ||
| /// In general, `C` is a concrete type, but it is also possible for it to be a trait object; | ||
| /// for more information, see [`IsCovariant`]. | ||
| /// In general, `C` is a concrete type, but it is also possible for it to be a trait object. | ||
| /// | ||
@@ -88,3 +86,6 @@ /// # Example | ||
| impl<Y: for<'a> Yokeable<'a>, C: StableDeref> Yoke<Y, C> { | ||
| impl<Y: for<'a> Yokeable<'a>, C: StableDeref> Yoke<Y, C> | ||
| where | ||
| <C as Deref>::Target: 'static, | ||
| { | ||
| /// Construct a [`Yoke`] by yokeing an object to a cart in a closure. | ||
@@ -122,3 +123,10 @@ /// | ||
| where | ||
| // safety note: This works by enforcing that the *only* place the return value of F | ||
| // can borrow from is the cart, since `F` must be valid for all lifetimes `'de` | ||
| // | ||
| // The <C as Deref>::Target: 'static on the impl is crucial for safety as well | ||
| // | ||
| // See safety docs at the bottom of this file for more information | ||
| F: for<'de> FnOnce(&'de <C as Deref>::Target) -> <Y as Yokeable<'de>>::Output, | ||
| <C as Deref>::Target: 'static, | ||
| { | ||
@@ -227,4 +235,6 @@ let deserialized = f(cart.deref()); | ||
| /// | ||
| /// let local_data = "foo".to_string(); | ||
| /// let yoke = Yoke::<&'static str, Box<String>>::attach_to_zero_copy_cart(Box::new(local_data)); | ||
| /// let local_data = "foo".to_owned(); | ||
| /// let yoke = Yoke::<&'static str, Box<String>>::attach_to_zero_copy_cart( | ||
| /// Box::new(local_data), | ||
| /// ); | ||
| /// assert_eq!(*yoke.get(), "foo"); | ||
@@ -243,5 +253,7 @@ /// | ||
| /// | ||
| /// let local_data = "foo".to_string(); | ||
| /// let local_data = "foo".to_owned(); | ||
| /// let mut yoke = | ||
| /// Yoke::<Cow<'static, str>, Box<String>>::attach_to_zero_copy_cart(Box::new(local_data)); | ||
| /// Yoke::<Cow<'static, str>, Box<String>>::attach_to_zero_copy_cart( | ||
| /// Box::new(local_data), | ||
| /// ); | ||
| /// assert_eq!(yoke.get(), "foo"); | ||
@@ -274,2 +286,5 @@ /// | ||
| /// returned cart type `C`. | ||
| /// - Lifetimes inside C must not be lengthened, even if they are themselves contravariant. | ||
| /// I.e., if C contains an `fn(&'a u8)`, it cannot be replaced with `fn(&'static u8), | ||
| /// even though that is typically safe. | ||
| /// | ||
@@ -516,8 +531,5 @@ /// Typically, this means implementing `f` as something which _wraps_ the inner cart type `C`. | ||
| // This is safe because Y is 'static and C has a covariant lifetime | ||
| unsafe impl<'b, Y: for<'a> Yokeable<'a>, C: IsCovariant<'b>> IsCovariant<'b> for Yoke<Y, C> {} | ||
| #[test] | ||
| fn test_clone() { | ||
| let local_data = "foo".to_string(); | ||
| let local_data = "foo".to_owned(); | ||
| let y1 = Yoke::<alloc::borrow::Cow<'static, str>, Rc<String>>::attach_to_zero_copy_cart( | ||
@@ -586,3 +598,5 @@ Rc::new(local_data), | ||
| /// | ||
| /// fn map_project_string_1(bar: Yoke<Bar<'static>, Rc<[u8]>>) -> Yoke<&'static str, Rc<[u8]>> { | ||
| /// fn map_project_string_1( | ||
| /// bar: Yoke<Bar<'static>, Rc<[u8]>>, | ||
| /// ) -> Yoke<&'static str, Rc<[u8]>> { | ||
| /// bar.map_project(|bar, _| bar.string_1) | ||
@@ -663,3 +677,5 @@ /// } | ||
| /// # | ||
| /// fn slice(y: Yoke<&'static [u8], Rc<[u8]>>) -> Result<Yoke<&'static str, Rc<[u8]>>, Utf8Error> { | ||
| /// fn slice( | ||
| /// y: Yoke<&'static [u8], Rc<[u8]>>, | ||
| /// ) -> Result<Yoke<&'static str, Rc<[u8]>>, Utf8Error> { | ||
| /// y.try_map_project(move |bytes, _| str::from_utf8(bytes)) | ||
@@ -684,3 +700,5 @@ /// } | ||
| /// | ||
| /// fn map_project_string_1(bar: Yoke<Bar<'static>, Rc<[u8]>>) -> Result<Yoke<&'static str, Rc<[u8]>>, Utf8Error> { | ||
| /// fn map_project_string_1( | ||
| /// bar: Yoke<Bar<'static>, Rc<[u8]>>, | ||
| /// ) -> Result<Yoke<&'static str, Rc<[u8]>>, Utf8Error> { | ||
| /// bar.try_map_project(|bar, _| str::from_utf8(bar.bytes_1)) | ||
@@ -875,3 +893,4 @@ /// } | ||
| /// | ||
| /// let yoke1 = Yoke::<&'static str, _>::attach_to_cart(buffer1, |rc| rc.trim()); | ||
| /// let yoke1 = | ||
| /// Yoke::<&'static str, _>::attach_to_cart(buffer1, |rc| rc.trim()); | ||
| /// let yoke2 = Yoke::<&'static str, _>::attach_to_cart(buffer2, |b| b.trim()); | ||
@@ -881,3 +900,4 @@ /// | ||
| /// // Wrap the Box in an Rc to make it compatible | ||
| /// let erased2: Yoke<_, ErasedRcCart> = yoke2.wrap_cart_in_rc().erase_rc_cart(); | ||
| /// let erased2: Yoke<_, ErasedRcCart> = | ||
| /// yoke2.wrap_cart_in_rc().erase_rc_cart(); | ||
| /// | ||
@@ -887,3 +907,3 @@ /// // Now erased1 and erased2 have the same type! | ||
| /// | ||
| /// Available with the `"alloc"` feature enabled. | ||
| /// Available with the `"alloc"` Cargo feature enabled. | ||
| pub fn erase_rc_cart(self) -> Yoke<Y, ErasedRcCart> { | ||
@@ -922,3 +942,4 @@ unsafe { | ||
| /// | ||
| /// let yoke1 = Yoke::<&'static str, _>::attach_to_cart(buffer1, |arc| arc.trim()); | ||
| /// let yoke1 = | ||
| /// Yoke::<&'static str, _>::attach_to_cart(buffer1, |arc| arc.trim()); | ||
| /// let yoke2 = Yoke::<&'static str, _>::attach_to_cart(buffer2, |b| b.trim()); | ||
@@ -928,3 +949,4 @@ /// | ||
| /// // Wrap the Box in an Rc to make it compatible | ||
| /// let erased2: Yoke<_, ErasedArcCart> = yoke2.wrap_cart_in_arc().erase_arc_cart(); | ||
| /// let erased2: Yoke<_, ErasedArcCart> = | ||
| /// yoke2.wrap_cart_in_arc().erase_arc_cart(); | ||
| /// | ||
@@ -934,3 +956,3 @@ /// // Now erased1 and erased2 have the same type! | ||
| /// | ||
| /// Available with the `"alloc"` feature enabled. | ||
| /// Available with the `"alloc"` Cargo feature enabled. | ||
| pub fn erase_arc_cart(self) -> Yoke<Y, ErasedArcCart> { | ||
@@ -969,7 +991,9 @@ unsafe { | ||
| /// | ||
| /// let yoke1 = Yoke::<&'static str, _>::attach_to_cart(buffer1, |rc| rc.trim()); | ||
| /// let yoke1 = | ||
| /// Yoke::<&'static str, _>::attach_to_cart(buffer1, |rc| rc.trim()); | ||
| /// let yoke2 = Yoke::<&'static str, _>::attach_to_cart(buffer2, |b| b.trim()); | ||
| /// | ||
| /// // Wrap the Rc in an Box to make it compatible | ||
| /// let erased1: Yoke<_, ErasedBoxCart> = yoke1.wrap_cart_in_box().erase_box_cart(); | ||
| /// let erased1: Yoke<_, ErasedBoxCart> = | ||
| /// yoke1.wrap_cart_in_box().erase_box_cart(); | ||
| /// let erased2: Yoke<_, ErasedBoxCart> = yoke2.erase_box_cart(); | ||
@@ -980,3 +1004,3 @@ /// | ||
| /// | ||
| /// Available with the `"alloc"` feature enabled. | ||
| /// Available with the `"alloc"` Cargo feature enabled. | ||
| pub fn erase_box_cart(self) -> Yoke<Y, ErasedBoxCart> { | ||
@@ -996,3 +1020,3 @@ unsafe { | ||
| /// | ||
| /// Available with the `"alloc"` feature enabled. | ||
| /// Available with the `"alloc"` Cargo feature enabled. | ||
| #[inline] | ||
@@ -1009,3 +1033,3 @@ pub fn wrap_cart_in_box(self) -> Yoke<Y, Box<C>> { | ||
| /// | ||
| /// Available with the `"alloc"` feature enabled. | ||
| /// Available with the `"alloc"` Cargo feature enabled. | ||
| #[inline] | ||
@@ -1022,3 +1046,3 @@ pub fn wrap_cart_in_rc(self) -> Yoke<Y, Rc<C>> { | ||
| /// | ||
| /// Available with the `"alloc"` feature enabled. | ||
| /// Available with the `"alloc"` Cargo feature enabled. | ||
| #[inline] | ||
@@ -1062,3 +1086,3 @@ pub fn wrap_cart_in_arc(self) -> Yoke<Y, Arc<C>> { | ||
| /// Safety docs for project() | ||
| /// # Safety docs for project() | ||
| /// | ||
@@ -1166,1 +1190,149 @@ /// (Docs are on a private const to allow the use of compile_fail doctests) | ||
| const _: () = (); | ||
| /// # Safety docs for attach_to_cart()'s signature | ||
| /// | ||
| /// The `attach_to_cart()` family of methods get by by using the following bound: | ||
| /// | ||
| /// ```rust,ignore | ||
| /// F: for<'de> FnOnce(&'de <C as Deref>::Target) -> <Y as Yokeable<'de>>::Output, | ||
| /// C::Target: 'static | ||
| /// ``` | ||
| /// | ||
| /// to enforce that the yoking closure produces a yokeable that is *only* allowed to borrow from the cart. | ||
| /// A way to be sure of this is as follows: imagine if `F` *did* borrow data of lifetime `'a` and stuff it in | ||
| /// its output. Then that lifetime `'a` would have to live at least as long as `'de` *for all `'de`*. | ||
| /// The only lifetime that satisfies that is `'static` (since at least one of the potential `'de`s is `'static`), | ||
| /// and we're fine with that. | ||
| /// | ||
| /// ## Implied bounds and variance | ||
| /// | ||
| /// The `C::Target: 'static` bound is tricky, however. Let's imagine a situation where we *didn't* have that bound. | ||
| /// | ||
| /// One thing to remember is that we are okay with the cart itself borrowing from places, | ||
| /// e.g. `&[u8]` is a valid cart, as is `Box<&[u8]>`. `C` is not `'static`. | ||
| /// | ||
| /// (I'm going to use `CT` in prose to refer to `C::Target` here, since almost everything here has to do | ||
| /// with C::Target and not C itself.) | ||
| /// | ||
| /// Unfortunately, there's a sneaky additional bound inside `F`. The signature of `F` is *actually* | ||
| /// | ||
| /// ```rust,ignore | ||
| /// F: for<'de> where<C::Target: 'de> FnOnce(&'de C::Target) -> <Y as Yokeable<'de>>::Output | ||
| /// ``` | ||
| /// | ||
| /// using made-up "where clause inside HRTB" syntax to represent a type that can be represented inside the compiler | ||
| /// and type system but not in Rust code. The `CT: 'de` bond comes from the `&'de C::Target`: any time you | ||
| /// write `&'a T`, an implied bound of `T: 'a` materializes and is stored alongside it, since references cannot refer | ||
| /// to data that itself refers to data of shorter lifetimes. If a reference is valid, its referent must be valid for | ||
| /// the duration of the reference's lifetime, so every reference *inside* its referent must also be valid, giving us `T: 'a`. | ||
| /// This kind of constraint is often called a "well formedness" constraint: `&'a T` is not "well formed" without that | ||
| /// bound, and rustc is being helpful by giving it to us for free. | ||
| /// | ||
| /// Unfortunately, this messes with our universal quantification. The `for<'de>` is no longer "For all lifetimes `'de`", | ||
| /// it is "for all lifetimes `'de` *where `CT: 'de`*". And if `CT` borrows from somewhere (with lifetime `'ct`), then we get a | ||
| /// `'ct: 'de` bound, and `'de` candidates that live longer than `'ct` won't actually be considered. | ||
| /// The neat little logic at the beginning stops working. | ||
| /// | ||
| /// `attach_to_cart()` will instead enforce that the produced yokeable *either* borrows from the cart (fine), or from | ||
| /// data that has a lifetime that is at least `'ct`. Which means that `attach_to_cart()` will allow us to borrow locals | ||
| /// provided they live at least as long as `'ct`. | ||
| /// | ||
| /// Is this a problem? | ||
| /// | ||
| /// This is totally fine if CT's lifetime is covariant: if C is something like `Box<&'ct [u8]>`, even if our | ||
| /// yoked object borrows from locals outliving `'ct`, our Yoke can't outlive that | ||
| /// lifetime `'ct` anyway (since it's a part of the cart type), so we're fine. | ||
| /// | ||
| /// However it's completely broken for contravariant carts (e.g. `Box<fn(&'ct u8)>`). In that case | ||
| /// we still get `'ct: 'de`, and we still end up being able to | ||
| /// borrow from locals that outlive `'ct`. However, our Yoke _can_ outlive | ||
| /// that lifetime, because Yoke shares its variance over `'ct` | ||
| /// with the cart type, and the cart type is contravariant over `'ct`. | ||
| /// So the Yoke can be upcast to having a longer lifetime than `'ct`, and *that* Yoke | ||
| /// can outlive `'ct`. | ||
| /// | ||
| /// We fix this by forcing `C::Target: 'static` in `attach_to_cart()`, which would make it work | ||
| /// for fewer types, but would also allow Yoke to continue to be covariant over cart lifetimes if necessary. | ||
| /// | ||
| /// An alternate fix would be to not allowing yoke to ever be upcast over lifetimes contained in the cart | ||
| /// by forcing them to be invariant. This is a bit more restrictive and affects *all* `Yoke` users, not just | ||
| /// those using `attach_to_cart()`. | ||
| /// | ||
| /// See https://github.com/unicode-org/icu4x/issues/2926 | ||
| /// See also https://github.com/rust-lang/rust/issues/106431 for potentially fixing this upstream by | ||
| /// changing how the bound works. | ||
| /// | ||
| /// # Tests | ||
| /// | ||
| /// Here's a broken `attach_to_cart()` that attempts to borrow from a local: | ||
| /// | ||
| /// ```rust,compile_fail | ||
| /// use yoke::{Yoke, Yokeable}; | ||
| /// | ||
| /// let cart = vec![1, 2, 3, 4].into_boxed_slice(); | ||
| /// let local = vec![4, 5, 6, 7]; | ||
| /// let yoke: Yoke<&[u8], Box<[u8]>> = Yoke::attach_to_cart(cart, |_| &*local); | ||
| /// ``` | ||
| /// | ||
| /// Fails as expected. | ||
| /// | ||
| /// And here's a working one with a local borrowed cart that does not do any sneaky borrows whilst attaching. | ||
| /// | ||
| /// ```rust | ||
| /// use yoke::{Yoke, Yokeable}; | ||
| /// | ||
| /// let cart = vec![1, 2, 3, 4].into_boxed_slice(); | ||
| /// let local = vec![4, 5, 6, 7]; | ||
| /// let yoke: Yoke<&[u8], &[u8]> = Yoke::attach_to_cart(&cart, |c| &*c); | ||
| /// ``` | ||
| /// | ||
| /// Here's an `attach_to_cart()` that attempts to borrow from a longer-lived local due to | ||
| /// the cart being covariant. It fails, but would not if the alternate fix of forcing Yoke to be invariant | ||
| /// were implemented. It is technically a safe operation: | ||
| /// | ||
| /// ```rust,compile_fail | ||
| /// use yoke::{Yoke, Yokeable}; | ||
| /// // longer lived | ||
| /// let local = vec![4, 5, 6, 7]; | ||
| /// | ||
| /// let backing = vec![1, 2, 3, 4]; | ||
| /// let cart = Box::new(&*backing); | ||
| /// | ||
| /// let yoke: Yoke<&[u8], Box<&[u8]>> = Yoke::attach_to_cart(cart, |_| &*local); | ||
| /// println!("{:?}", yoke.get()); | ||
| /// ``` | ||
| /// | ||
| /// Finally, here's an `attach_to_cart()` that attempts to borrow from a longer lived local | ||
| /// in the case of a contravariant lifetime. It does not compile, but in and of itself is not dangerous: | ||
| /// | ||
| /// ```rust,compile_fail | ||
| /// use yoke::Yoke; | ||
| /// | ||
| /// type Contra<'a> = fn(&'a ()); | ||
| /// | ||
| /// let local = String::from("Hello World!"); | ||
| /// let yoke: Yoke<&'static str, Box<Contra<'_>>> = Yoke::attach_to_cart(Box::new((|_| {}) as _), |_| &local[..]); | ||
| /// println!("{:?}", yoke.get()); | ||
| /// ``` | ||
| /// | ||
| /// It is dangerous if allowed to transform (testcase from #2926) | ||
| /// | ||
| /// ```rust,compile_fail | ||
| /// use yoke::Yoke; | ||
| /// | ||
| /// type Contra<'a> = fn(&'a ()); | ||
| /// | ||
| /// | ||
| /// let local = String::from("Hello World!"); | ||
| /// let yoke: Yoke<&'static str, Box<Contra<'_>>> = Yoke::attach_to_cart(Box::new((|_| {}) as _), |_| &local[..]); | ||
| /// println!("{:?}", yoke.get()); | ||
| /// let yoke_longer: Yoke<&'static str, Box<Contra<'static>>> = yoke; | ||
| /// let leaked: &'static Yoke<&'static str, Box<Contra<'static>>> = Box::leak(Box::new(yoke_longer)); | ||
| /// let reference: &'static str = leaked.get(); | ||
| /// | ||
| /// println!("pre-drop: {reference}"); | ||
| /// drop(local); | ||
| /// println!("post-drop: {reference}"); | ||
| /// | ||
| /// ``` | ||
| const _: () = (); |
+4
-1
@@ -29,2 +29,3 @@ // This file is part of ICU4X. For terms of use, please see the file | ||
| C: StableDeref + Deref, | ||
| <C as Deref>::Target: 'static, | ||
| { | ||
@@ -46,3 +47,5 @@ /// Construct a [`Yoke`]`<Y, C>` from a cart implementing `StableDeref` by zero-copy cloning | ||
| /// | ||
| /// let yoke = Yoke::<Cow<'static, str>, String>::attach_to_zero_copy_cart("demo".to_string()); | ||
| /// let yoke = Yoke::<Cow<'static, str>, String>::attach_to_zero_copy_cart( | ||
| /// "demo".to_owned(), | ||
| /// ); | ||
| /// | ||
@@ -49,0 +52,0 @@ /// assert_eq!("demo", yoke.get()); |
| // This file is part of ICU4X. For terms of use, please see the file | ||
| // called LICENSE at the top level of the ICU4X source tree | ||
| // (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ). | ||
| #[cfg(feature = "alloc")] | ||
| use alloc::{ | ||
| borrow::{Cow, ToOwned}, | ||
| boxed::Box, | ||
| rc::Rc, | ||
| string::String, | ||
| }; | ||
| /// A type implementing `IsCovariant<'a>` is covariant with respect to lifetime `'a`. | ||
| /// | ||
| /// Lifetime parameters that are safely cast in [`Yokeable`] are also valid for `IsCovariant`. | ||
| /// | ||
| /// `IsCovariant` exists primarily to serve in trait bounds. The primary use case is to safely | ||
| /// perform lifetime casting on trait objects (`dyn Trait`). This enables a type-erased [`Yoke`] | ||
| /// consisting of only trait objects. See the examples. | ||
| /// | ||
| /// `IsCovariant` is auto-implemented in [`#[derive(Yokeable)]`](macro@crate::Yokeable). | ||
| /// | ||
| /// # Safety | ||
| /// | ||
| /// This trait is safe to implement on types with a _covariant_ lifetime parameter. This will | ||
| /// occur when the lifetime parameter is used within references, but not in the arguments of | ||
| /// function pointers or in mutable positions (either in `&mut` or via interior mutability). | ||
| /// | ||
| /// If a struct has multiple lifetime parameters, only the one used in `IsCovariant<'a>` needs to | ||
| /// be covariant. | ||
| /// | ||
| /// # Examples | ||
| /// | ||
| /// Implementing on a simple struct with a single covariant lifetime: | ||
| /// | ||
| /// ``` | ||
| /// # use yoke::*; | ||
| /// struct MyStruct<'a>(&'a str); | ||
| /// | ||
| /// // This is safe because 'a is covariant | ||
| /// unsafe impl<'a> IsCovariant<'a> for MyStruct<'a> {} | ||
| /// ``` | ||
| /// | ||
| /// By constraining the trait `ExampleTrait<'a>` on `IsCovariant<'a>`, we can safely implement | ||
| /// [`Yokeable`] and [`ZeroFrom`] on its trait object: | ||
| /// | ||
| /// ``` | ||
| /// # use yoke::*; | ||
| /// # use zerofrom::*; | ||
| /// # use core::mem; | ||
| /// trait ExampleTrait<'a>: IsCovariant<'a> { | ||
| /// fn get_message(&self) -> &'a str; | ||
| /// } | ||
| /// | ||
| /// // This wrapper is required because of the blanket Yokeable impl on &'static T | ||
| /// pub struct ExampleTraitDynRef<'a>(pub &'a dyn ExampleTrait<'a>); | ||
| /// | ||
| /// // The following impl is safe because the trait object requires IsCovariant. | ||
| /// unsafe impl<'a> Yokeable<'a> for ExampleTraitDynRef<'static> { | ||
| /// type Output = ExampleTraitDynRef<'a>; | ||
| /// fn transform(&'a self) -> &'a Self::Output { | ||
| /// unsafe { mem::transmute(self) } | ||
| /// } | ||
| /// | ||
| /// fn transform_owned(self) -> Self::Output { | ||
| /// unsafe { mem::transmute(self) } | ||
| /// } | ||
| /// | ||
| /// unsafe fn make(from: Self::Output) -> Self { | ||
| /// unsafe { mem::transmute(from) } | ||
| /// } | ||
| /// | ||
| /// fn transform_mut<F>(&'a mut self, f: F) | ||
| /// where | ||
| /// F: 'static + FnOnce(&'a mut Self::Output), | ||
| /// { | ||
| /// unsafe { f(mem::transmute::<&mut Self, &mut Self::Output>(self)) } | ||
| /// } | ||
| /// } | ||
| /// | ||
| /// impl<'zf, 'a> ZeroFrom<'zf, dyn ExampleTrait<'a> + 'a> for ExampleTraitDynRef<'zf> { | ||
| /// fn zero_from(this: &'zf (dyn ExampleTrait<'a> + 'a)) -> ExampleTraitDynRef<'zf> { | ||
| /// // This is safe because the trait object requires IsCovariant. | ||
| /// ExampleTraitDynRef(unsafe { core::mem::transmute(this) }) | ||
| /// } | ||
| /// } | ||
| /// | ||
| /// // Implement ExampleTrait on the struct from the previous example | ||
| /// # struct MyStruct<'a>(&'a str); | ||
| /// # unsafe impl<'a> IsCovariant<'a> for MyStruct<'a> {} | ||
| /// impl<'a> ExampleTrait<'a> for MyStruct<'a> { | ||
| /// fn get_message(&self) -> &'a str { | ||
| /// self.0 | ||
| /// } | ||
| /// } | ||
| /// | ||
| /// // Example usage: a Yoke of a trait object | ||
| /// let s = "Hello World".to_string(); | ||
| /// let yoke: Yoke<ExampleTraitDynRef<'static>, Box<dyn ExampleTrait>> = | ||
| /// Yoke::attach_to_zero_copy_cart(Box::new(MyStruct(&s))); | ||
| /// | ||
| /// assert_eq!(yoke.get().0.get_message(), "Hello World"); | ||
| /// ``` | ||
| /// | ||
| /// [`Yoke`]: crate::Yoke | ||
| /// [`Yokeable`]: crate::Yokeable | ||
| /// [`ZeroFrom`]: crate::ZeroFrom | ||
| pub unsafe trait IsCovariant<'a>: 'a {} | ||
| // IsCovariant is implemented on the standard library Copy types in macro_impls.rs | ||
| // The following impls are safe because there is only one lifetime, 'a, and 'a is covariant | ||
| unsafe impl<'a> IsCovariant<'a> for () {} | ||
| unsafe impl<'a> IsCovariant<'a> for str {} | ||
| #[cfg(feature = "alloc")] | ||
| unsafe impl<'a> IsCovariant<'a> for String {} | ||
| unsafe impl<'a, T: IsCovariant<'a>> IsCovariant<'a> for Option<T> {} | ||
| unsafe impl<'a, T1: IsCovariant<'a>, T2: IsCovariant<'a>> IsCovariant<'a> for (T1, T2) {} | ||
| unsafe impl<'a, T: IsCovariant<'a>> IsCovariant<'a> for [T] {} | ||
| unsafe impl<'a, T: IsCovariant<'a>, const N: usize> IsCovariant<'a> for [T; N] {} | ||
| #[cfg(feature = "alloc")] | ||
| unsafe impl<'a, T: IsCovariant<'a> + ?Sized> IsCovariant<'a> for Box<T> {} | ||
| #[cfg(feature = "alloc")] | ||
| unsafe impl<'a, T: IsCovariant<'a> + ?Sized> IsCovariant<'a> for Rc<T> {} | ||
| // This is safe because T has a covariant lifetime, and Cow's lifetime is also covariant | ||
| #[cfg(feature = "alloc")] | ||
| unsafe impl<'a, T: IsCovariant<'a> + ToOwned + ?Sized> IsCovariant<'a> for Cow<'a, T> where | ||
| <T as ToOwned>::Owned: Sized | ||
| { | ||
| } | ||
| // This is safe because T has a covariant lifetime, and the reference lifetime is also covariant | ||
| unsafe impl<'a, T: IsCovariant<'a> + ?Sized> IsCovariant<'a> for &'a T {} |
Sorry, the diff of this file is not supported yet