| // 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 ). |
| |
| //! Workarounds for adding trait bounds to `yoke` objects. |
| //! |
| //! # Trait bounds in Yoke |
| //! |
| //! [Compiler bug #89196](https://github.com/rust-lang/rust/issues/89196) makes it tricky to add |
| //! trait bounds involving `yoke` types. |
| //! |
| //! For example, you may want to write: |
| //! |
| //! `where for<'a> <Y as Yokeable<'a>>::Output: MyTrait` |
| //! |
| //! The above trait bound will compile, but at call sites, you get errors such as: |
| //! |
| //! > the trait `for<'de> MyTrait` is not implemented for `<Y as Yokeable<'de>>::Output` |
| //! |
| //! There are two known workarounds: |
| //! |
| //! 1. If the trait is well-defined on references, like `Debug`, bind the trait to a reference: |
| //! `where for<'a> &'a <Y as Yokeable<'a>>::Output: MyTrait` |
| //! 2. If the trait involves `Self`, like `Clone`, use [`YokeTraitHack`]: |
| //! `where for<'a> YokeTraitHack<<Y as Yokeable<'a>>::Output>: MyTrait` |
| //! |
| //! # Examples |
| //! |
| //! Code that does not compile ([playground](https://play.rust-lang.org/?version=beta&mode=debug&edition=2018&gist=ebbda5b15a398d648bdff9e439b27dc0)): |
| //! |
| //! ```compile_fail |
| //! # this compiles in 1.78+, so this text will make it fail |
| //! use yoke::*; |
| //! |
| //! trait MiniDataMarker { |
| //! type Yokeable: for<'a> Yokeable<'a>; |
| //! } |
| //! |
| //! struct MiniDataPayload<M> |
| //! where |
| //! M: MiniDataMarker |
| //! { |
| //! pub yoke: Yoke<M::Yokeable, ()>, |
| //! } |
| //! |
| //! impl<M> Clone for MiniDataPayload<M> |
| //! where |
| //! M: MiniDataMarker, |
| //! for<'a> <M::Yokeable as Yokeable<'a>>::Output: Clone, |
| //! { |
| //! fn clone(&self) -> Self { |
| //! unimplemented!() |
| //! } |
| //! } |
| //! |
| //! trait MiniDataProvider<M> |
| //! where |
| //! M: MiniDataMarker |
| //! { |
| //! fn mini_load_data(&self) -> MiniDataPayload<M>; |
| //! } |
| //! |
| //! struct MiniStructProvider<M> |
| //! where |
| //! M: MiniDataMarker, |
| //! { |
| //! pub payload: MiniDataPayload<M>, |
| //! } |
| //! |
| //! impl<M> MiniDataProvider<M> for MiniStructProvider<M> |
| //! where |
| //! M: MiniDataMarker, |
| //! for<'a> <M::Yokeable as Yokeable<'a>>::Output: Clone, |
| //! { |
| //! fn mini_load_data(&self) -> MiniDataPayload<M> { |
| //! self.payload.clone() |
| //! } |
| //! } |
| //! |
| //! #[derive(Clone)] |
| //! struct SimpleStruct(pub u32); |
| //! |
| //! unsafe impl<'a> Yokeable<'a> for SimpleStruct { |
| //! // (not shown; see `Yokeable` for examples) |
| //! # type Output = SimpleStruct; |
| //! # fn transform(&'a self) -> &'a Self::Output { |
| //! # self |
| //! # } |
| //! # fn transform_owned(self) -> Self::Output { |
| //! # self |
| //! # } |
| //! # unsafe fn make(from: Self::Output) -> Self { |
| //! # std::mem::transmute(from) |
| //! # } |
| //! # fn transform_mut<F>(&'a mut self, f: F) |
| //! # where |
| //! # F: 'static + for<'b> FnOnce(&'b mut Self::Output), |
| //! # { |
| //! # unsafe { |
| //! # f(std::mem::transmute::<&'a mut Self, &'a mut Self::Output>( |
| //! # self, |
| //! # )) |
| //! # } |
| //! # } |
| //! } |
| //! |
| //! impl MiniDataMarker for SimpleStruct { |
| //! type Yokeable = SimpleStruct; |
| //! } |
| //! |
| //! let provider = MiniStructProvider { |
| //! payload: MiniDataPayload { |
| //! yoke: Yoke::new_always_owned(SimpleStruct(42)) |
| //! } |
| //! }; |
| //! |
| //! // Broken: |
| //! // "method cannot be called on `MiniStructProvider<_>` due to unsatisfied trait bounds" |
| //! let payload: MiniDataPayload<SimpleStruct> = provider.mini_load_data(); |
| //! |
| //! // Working: |
| //! let payload = MiniDataProvider::<SimpleStruct>::mini_load_data(&provider); |
| //! |
| //! assert_eq!(payload.yoke.get().0, 42); |
| //! ``` |
| //! |
| //! Example for binding the trait to a reference: |
| //! |
| //! ``` |
| //! use yoke::Yoke; |
| //! use yoke::Yokeable; |
| //! |
| //! // Example trait and struct for illustration purposes: |
| //! trait MyTrait { |
| //! fn demo(&self) -> u32; |
| //! } |
| //! struct MyStruct(u32); |
| //! impl MyTrait for MyStruct { |
| //! fn demo(&self) -> u32 { |
| //! self.0 |
| //! } |
| //! } |
| //! unsafe impl<'a> Yokeable<'a> for MyStruct { |
| //! // (not shown; see `Yokeable` for examples) |
| //! # type Output = MyStruct; |
| //! # fn transform(&'a self) -> &'a Self::Output { |
| //! # self |
| //! # } |
| //! # fn transform_owned(self) -> Self::Output { |
| //! # self |
| //! # } |
| //! # unsafe fn make(from: Self::Output) -> Self { |
| //! # std::mem::transmute(from) |
| //! # } |
| //! # fn transform_mut<F>(&'a mut self, f: F) |
| //! # where |
| //! # F: 'static + for<'b> FnOnce(&'b mut Self::Output), |
| //! # { |
| //! # unsafe { |
| //! # f(std::mem::transmute::<&'a mut Self, &'a mut Self::Output>( |
| //! # self, |
| //! # )) |
| //! # } |
| //! # } |
| //! } |
| //! |
| //! // The trait needs to be defined on references: |
| //! impl<'a, T> MyTrait for &'a T |
| //! where |
| //! T: MyTrait, |
| //! { |
| //! fn demo(&self) -> u32 { |
| //! self.demo() |
| //! } |
| //! } |
| //! |
| //! impl<Y, C> MyTrait for Yoke<Y, C> |
| //! where |
| //! Y: for<'a> Yokeable<'a>, |
| //! for<'a> &'a <Y as Yokeable<'a>>::Output: MyTrait, |
| //! { |
| //! fn demo(&self) -> u32 { |
| //! self.get().demo() |
| //! } |
| //! } |
| //! |
| //! fn example() { |
| //! let y = Yoke::<MyStruct, ()>::new_always_owned(MyStruct(42)); |
| //! let _: &dyn MyTrait = &y; |
| //! } |
| //! ``` |
| //! |
| //! Example for using [`YokeTraitHack`]: |
| //! |
| //! ``` |
| //! use std::rc::Rc; |
| //! use yoke::trait_hack::YokeTraitHack; |
| //! use yoke::Yoke; |
| //! use yoke::Yokeable; |
| //! |
| //! // Example trait and struct for illustration purposes: |
| //! trait MyTrait { |
| //! fn demo(data: u32) -> Self; |
| //! } |
| //! struct MyStruct(u32); |
| //! impl MyTrait for MyStruct { |
| //! fn demo(data: u32) -> Self { |
| //! Self(data) |
| //! } |
| //! } |
| //! unsafe impl<'a> Yokeable<'a> for MyStruct { |
| //! // (not shown; see `Yokeable` for examples) |
| //! # type Output = MyStruct; |
| //! # fn transform(&'a self) -> &'a Self::Output { |
| //! # self |
| //! # } |
| //! # fn transform_owned(self) -> Self::Output { |
| //! # self |
| //! # } |
| //! # unsafe fn make(from: Self::Output) -> Self { |
| //! # std::mem::transmute(from) |
| //! # } |
| //! # fn transform_mut<F>(&'a mut self, f: F) |
| //! # where |
| //! # F: 'static + for<'b> FnOnce(&'b mut Self::Output), |
| //! # { |
| //! # unsafe { |
| //! # f(std::mem::transmute::<&'a mut Self, &'a mut Self::Output>( |
| //! # self, |
| //! # )) |
| //! # } |
| //! # } |
| //! } |
| //! |
| //! // The trait needs to be defined on YokeTraitHack: |
| //! impl<'a, T> MyTrait for YokeTraitHack<T> |
| //! where |
| //! T: MyTrait, |
| //! { |
| //! fn demo(data: u32) -> Self { |
| //! YokeTraitHack(T::demo(data)) |
| //! } |
| //! } |
| //! |
| //! impl<Y> MyTrait for Yoke<Y, Rc<u32>> |
| //! where |
| //! Y: for<'a> Yokeable<'a>, |
| //! for<'a> YokeTraitHack<<Y as Yokeable<'a>>::Output>: MyTrait, |
| //! { |
| //! fn demo(data: u32) -> Self { |
| //! let rc_u32: Rc<u32> = Rc::new(data); |
| //! Yoke::attach_to_cart(rc_u32, |u| { |
| //! YokeTraitHack::<<Y as Yokeable>::Output>::demo(*u).0 |
| //! }) |
| //! } |
| //! } |
| //! |
| //! fn example() { |
| //! let _ = Yoke::<MyStruct, Rc<u32>>::demo(42); |
| //! } |
| //! ``` |
| |
| use core::mem; |
| |
| /// A wrapper around a type `T`, forwarding trait calls down to the inner type. |
| /// |
| /// `YokeTraitHack` supports [`Clone`], [`PartialEq`], [`Eq`], and [`serde::Deserialize`] out of |
| /// the box. Other traits can be implemented by the caller. |
| /// |
| /// For more information, see the module-level documentation. |
| /// |
| /// # Example |
| /// |
| /// Using `YokeTraitHack` as a type bound in a function comparing two `Yoke`s: |
| /// |
| /// ``` |
| /// use yoke::trait_hack::YokeTraitHack; |
| /// use yoke::*; |
| /// |
| /// fn compare_yokes<Y, C1, C2>(y1: Yoke<Y, C1>, y2: Yoke<Y, C2>) -> bool |
| /// where |
| /// Y: for<'a> Yokeable<'a>, |
| /// for<'a> YokeTraitHack<<Y as Yokeable<'a>>::Output>: PartialEq, |
| /// { |
| /// YokeTraitHack(y1.get()).into_ref() == YokeTraitHack(y2.get()).into_ref() |
| /// } |
| /// ``` |
| #[repr(transparent)] |
| #[derive(Clone, PartialEq, Eq, Debug)] |
| #[allow(clippy::exhaustive_structs)] // newtype |
| pub struct YokeTraitHack<T>(pub T); |
| |
| impl<'a, T> YokeTraitHack<&'a T> { |
| /// Converts from `YokeTraitHack<&T>` to `&YokeTraitHack<T>`. |
| /// |
| /// This is safe because `YokeTraitHack` is `repr(transparent)`. |
| /// |
| /// This method is required to implement `Clone` on `Yoke`. |
| pub fn into_ref(self) -> &'a YokeTraitHack<T> { |
| // YokeTraitHack is repr(transparent) so it's always safe |
| // to transmute YTH<&T> to &YTH<T> |
| unsafe { mem::transmute::<YokeTraitHack<&T>, &YokeTraitHack<T>>(self) } |
| } |
| } |
| |
| // This is implemented manually to avoid the serde derive dependency. |
| #[cfg(feature = "serde")] |
| impl<'de, T> serde::de::Deserialize<'de> for YokeTraitHack<T> |
| where |
| T: serde::de::Deserialize<'de>, |
| { |
| #[inline] |
| fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> |
| where |
| D: serde::de::Deserializer<'de>, |
| { |
| T::deserialize(deserializer).map(YokeTraitHack) |
| } |
| } |