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 | |
263 | use 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 |
291 | pub struct YokeTraitHack<T>(pub T); |
292 | |
293 | impl<'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" )] |
308 | impl<'de, T> serde::de::Deserialize<'de> for YokeTraitHack<T> |
309 | where |
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 | } |
320 | |