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:

  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):

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 DataStruct = 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.