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