yoke/
trait_hack.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
5//! Workarounds for adding trait bounds to `yoke` objects.
6//!
7//! # Trait bounds in Yoke
8//!
9//! [Compiler bug #89196](https://github.com/rust-lang/rust/issues/89196) makes it tricky to add
10//! trait bounds involving `yoke` types.
11//!
12//! For example, you may want to write:
13//!
14//! `where for<'a> <Y as Yokeable<'a>>::Output: MyTrait`
15//!
16//! The above trait bound will compile, but at call sites, you get errors such as:
17//!
18//! > the trait `for<'de> MyTrait` is not implemented for `<Y as Yokeable<'de>>::Output`
19//!
20//! There are two known workarounds:
21//!
22//! 1. If the trait is well-defined on references, like `Debug`, bind the trait to a reference:
23//!     `where for<'a> &'a <Y as Yokeable<'a>>::Output: MyTrait`
24//! 2. If the trait involves `Self`, like `Clone`, use [`YokeTraitHack`]:
25//!     `where for<'a> YokeTraitHack<<Y as Yokeable<'a>>::Output>: MyTrait`
26//!
27//! # Examples
28//!
29//! Code that does not compile ([playground](https://play.rust-lang.org/?version=beta&mode=debug&edition=2018&gist=ebbda5b15a398d648bdff9e439b27dc0)):
30//!
31//! ```compile_fail
32//! # this compiles in 1.78+, so this text will make it fail
33//! use yoke::*;
34//!
35//! trait MiniDataMarker {
36//!     type Yokeable: for<'a> Yokeable<'a>;
37//! }
38//!
39//! struct MiniDataPayload<M>
40//! where
41//!     M: MiniDataMarker
42//! {
43//!     pub yoke: Yoke<M::Yokeable, ()>,
44//! }
45//!
46//! impl<M> Clone for MiniDataPayload<M>
47//! where
48//!     M: MiniDataMarker,
49//!     for<'a> <M::Yokeable as Yokeable<'a>>::Output: Clone,
50//! {
51//!     fn clone(&self) -> Self {
52//!         unimplemented!()
53//!     }
54//! }
55//!
56//! trait MiniDataProvider<M>
57//! where
58//!     M: MiniDataMarker
59//! {
60//!     fn mini_load_data(&self) -> MiniDataPayload<M>;
61//! }
62//!
63//! struct MiniStructProvider<M>
64//! where
65//!     M: MiniDataMarker,
66//! {
67//!     pub payload: MiniDataPayload<M>,
68//! }
69//!
70//! impl<M> MiniDataProvider<M> for MiniStructProvider<M>
71//! where
72//!     M: MiniDataMarker,
73//!     for<'a> <M::Yokeable as Yokeable<'a>>::Output: Clone,
74//! {
75//!     fn mini_load_data(&self) -> MiniDataPayload<M> {
76//!         self.payload.clone()
77//!     }
78//! }
79//!
80//! #[derive(Clone)]
81//! struct SimpleStruct(pub u32);
82//!
83//! unsafe impl<'a> Yokeable<'a> for SimpleStruct {
84//!     // (not shown; see `Yokeable` for examples)
85//! #    type Output = SimpleStruct;
86//! #    fn transform(&'a self) -> &'a Self::Output {
87//! #        self
88//! #    }
89//! #    fn transform_owned(self) -> Self::Output {
90//! #        self
91//! #    }
92//! #    unsafe fn make(from: Self::Output) -> Self {
93//! #        std::mem::transmute(from)
94//! #    }
95//! #    fn transform_mut<F>(&'a mut self, f: F)
96//! #    where
97//! #        F: 'static + for<'b> FnOnce(&'b mut Self::Output),
98//! #    {
99//! #        unsafe {
100//! #            f(std::mem::transmute::<&'a mut Self, &'a mut Self::Output>(
101//! #                self,
102//! #            ))
103//! #        }
104//! #    }
105//! }
106//!
107//! impl MiniDataMarker for SimpleStruct {
108//!     type DataStruct = SimpleStruct;
109//! }
110//!
111//! let provider = MiniStructProvider {
112//!     payload: MiniDataPayload {
113//!         yoke: Yoke::new_always_owned(SimpleStruct(42))
114//!     }
115//! };
116//!
117//! // Broken:
118//! // "method cannot be called on `MiniStructProvider<_>` due to unsatisfied trait bounds"
119//! let payload: MiniDataPayload<SimpleStruct> = provider.mini_load_data();
120//!
121//! // Working:
122//! let payload = MiniDataProvider::<SimpleStruct>::mini_load_data(&provider);
123//!
124//! assert_eq!(payload.yoke.get().0, 42);
125//! ```
126//!
127//! Example for binding the trait to a reference:
128//!
129//! ```
130//! use yoke::Yoke;
131//! use yoke::Yokeable;
132//!
133//! // Example trait and struct for illustration purposes:
134//! trait MyTrait {
135//!     fn demo(&self) -> u32;
136//! }
137//! struct MyStruct(u32);
138//! impl MyTrait for MyStruct {
139//!     fn demo(&self) -> u32 {
140//!         self.0
141//!     }
142//! }
143//! unsafe impl<'a> Yokeable<'a> for MyStruct {
144//!     // (not shown; see `Yokeable` for examples)
145//! #    type Output = MyStruct;
146//! #    fn transform(&'a self) -> &'a Self::Output {
147//! #        self
148//! #    }
149//! #    fn transform_owned(self) -> Self::Output {
150//! #        self
151//! #    }
152//! #    unsafe fn make(from: Self::Output) -> Self {
153//! #        std::mem::transmute(from)
154//! #    }
155//! #    fn transform_mut<F>(&'a mut self, f: F)
156//! #    where
157//! #        F: 'static + for<'b> FnOnce(&'b mut Self::Output),
158//! #    {
159//! #        unsafe {
160//! #            f(std::mem::transmute::<&'a mut Self, &'a mut Self::Output>(
161//! #                self,
162//! #            ))
163//! #        }
164//! #    }
165//! }
166//!
167//! // The trait needs to be defined on references:
168//! impl<'a, T> MyTrait for &'a T
169//! where
170//!     T: MyTrait,
171//! {
172//!     fn demo(&self) -> u32 {
173//!         self.demo()
174//!     }
175//! }
176//!
177//! impl<Y, C> MyTrait for Yoke<Y, C>
178//! where
179//!     Y: for<'a> Yokeable<'a>,
180//!     for<'a> &'a <Y as Yokeable<'a>>::Output: MyTrait,
181//! {
182//!     fn demo(&self) -> u32 {
183//!         self.get().demo()
184//!     }
185//! }
186//!
187//! fn example() {
188//!     let y = Yoke::<MyStruct, ()>::new_always_owned(MyStruct(42));
189//!     let _: &dyn MyTrait = &y;
190//! }
191//! ```
192//!
193//! Example for using [`YokeTraitHack`]:
194//!
195//! ```
196//! use std::rc::Rc;
197//! use yoke::trait_hack::YokeTraitHack;
198//! use yoke::Yoke;
199//! use yoke::Yokeable;
200//!
201//! // Example trait and struct for illustration purposes:
202//! trait MyTrait {
203//!     fn demo(data: u32) -> Self;
204//! }
205//! struct MyStruct(u32);
206//! impl MyTrait for MyStruct {
207//!     fn demo(data: u32) -> Self {
208//!         Self(data)
209//!     }
210//! }
211//! unsafe impl<'a> Yokeable<'a> for MyStruct {
212//!     // (not shown; see `Yokeable` for examples)
213//! #    type Output = MyStruct;
214//! #    fn transform(&'a self) -> &'a Self::Output {
215//! #        self
216//! #    }
217//! #    fn transform_owned(self) -> Self::Output {
218//! #        self
219//! #    }
220//! #    unsafe fn make(from: Self::Output) -> Self {
221//! #        std::mem::transmute(from)
222//! #    }
223//! #    fn transform_mut<F>(&'a mut self, f: F)
224//! #    where
225//! #        F: 'static + for<'b> FnOnce(&'b mut Self::Output),
226//! #    {
227//! #        unsafe {
228//! #            f(std::mem::transmute::<&'a mut Self, &'a mut Self::Output>(
229//! #                self,
230//! #            ))
231//! #        }
232//! #    }
233//! }
234//!
235//! // The trait needs to be defined on YokeTraitHack:
236//! impl<'a, T> MyTrait for YokeTraitHack<T>
237//! where
238//!     T: MyTrait,
239//! {
240//!     fn demo(data: u32) -> Self {
241//!         YokeTraitHack(T::demo(data))
242//!     }
243//! }
244//!
245//! impl<Y> MyTrait for Yoke<Y, Rc<u32>>
246//! where
247//!     Y: for<'a> Yokeable<'a>,
248//!     for<'a> YokeTraitHack<<Y as Yokeable<'a>>::Output>: MyTrait,
249//! {
250//!     fn demo(data: u32) -> Self {
251//!         let rc_u32: Rc<u32> = Rc::new(data);
252//!         Yoke::attach_to_cart(rc_u32, |u| {
253//!             YokeTraitHack::<<Y as Yokeable>::Output>::demo(*u).0
254//!         })
255//!     }
256//! }
257//!
258//! fn example() {
259//!     let _ = Yoke::<MyStruct, Rc<u32>>::demo(42);
260//! }
261//! ```
262
263use core::mem;
264
265/// A wrapper around a type `T`, forwarding trait calls down to the inner type.
266///
267/// `YokeTraitHack` supports [`Clone`], [`PartialEq`], [`Eq`], and [`serde::Deserialize`] out of
268/// the box. Other traits can be implemented by the caller.
269///
270/// For more information, see the module-level documentation.
271///
272/// # Example
273///
274/// Using `YokeTraitHack` as a type bound in a function comparing two `Yoke`s:
275///
276/// ```
277/// use yoke::trait_hack::YokeTraitHack;
278/// use yoke::*;
279///
280/// fn compare_yokes<Y, C1, C2>(y1: Yoke<Y, C1>, y2: Yoke<Y, C2>) -> bool
281/// where
282///     Y: for<'a> Yokeable<'a>,
283///     for<'a> YokeTraitHack<<Y as Yokeable<'a>>::Output>: PartialEq,
284/// {
285///     YokeTraitHack(y1.get()).into_ref() == YokeTraitHack(y2.get()).into_ref()
286/// }
287/// ```
288#[repr(transparent)]
289#[derive(Clone, PartialEq, Eq, Debug)]
290#[allow(clippy::exhaustive_structs)] // newtype
291pub struct YokeTraitHack<T>(pub T);
292
293impl<'a, T> YokeTraitHack<&'a T> {
294    /// Converts from `YokeTraitHack<&T>` to `&YokeTraitHack<T>`.
295    ///
296    /// This is safe because `YokeTraitHack` is `repr(transparent)`.
297    ///
298    /// This method is required to implement `Clone` on `Yoke`.
299    pub fn into_ref(self) -> &'a YokeTraitHack<T> {
300        // Safety: YokeTraitHack is repr(transparent) so it's always safe
301        // to transmute YTH<&T> to &YTH<T>
302        unsafe { mem::transmute::<YokeTraitHack<&T>, &YokeTraitHack<T>>(self) }
303    }
304}
305
306// This is implemented manually to avoid the serde derive dependency.
307#[cfg(feature = "serde")]
308impl<'de, T> serde::de::Deserialize<'de> for YokeTraitHack<T>
309where
310    T: serde::de::Deserialize<'de>,
311{
312    #[inline]
313    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
314    where
315        D: serde::de::Deserializer<'de>,
316    {
317        T::deserialize(deserializer).map(YokeTraitHack)
318    }
319}