Module yoke::trait_hack
source · Expand description
Workarounds for adding trait bounds to yoke
objects.
§Trait bounds in Yoke
Compiler bug #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:
- 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
- If the trait involves
Self
, likeClone
, useYokeTraitHack
:where for<'a> YokeTraitHack<<Y as Yokeable<'a>>::Output>: MyTrait
§Examples
Code that does not compile (playground):
ⓘ
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)
}
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)
}
// 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)
}
// 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);
}
Structs§
- A wrapper around a type
T
, forwarding trait calls down to the inner type.