Emulating type families with relational traits

I was inspired by this post to try to come up with a Rust equivalent. Indeed, I think it's possible to use traits to model arbitrary relational restrictions.

I'm still a newb at type system hacking, but I thought this was cool.

trait Person {}
trait Item {}

struct Warrior {}
impl Person for Warrior {}

struct Wizard {}
impl Person for Wizard {}

struct Sword {}
impl Item for Sword {}

struct Wand {}
impl Item for Wand {}

trait Wield<P: Person, I: Item> {
    // this function is not meant to be used, but I believe is required to make the `P: Person, I: Item` trait restrictions stick
    fn _f(p: P, i: I) {}
}

impl Wield<Warrior, Sword> for (Warrior, Sword) {
    fn _f(p: Warrior, i: Sword) {}
}

impl Wield<Wizard, Wand> for (Wizard, Wand) {
    fn _f(p: Wizard, i: Wand) {}
}

impl Wield<Wizard, Sword> for (Wizard, Sword) {
    fn _f(p: Wizard, i: Sword) {}
}

struct Hero<P, I>
where
    P: Person,
    I: Item,
    (P, I): Wield<P, I>,
{
    p: P,
    i: I,
}

fn main() {
    let a = Hero {
        p: Warrior {},
        i: Sword {},
    };
    let b = Hero {
        p: Wizard {},
        i: Sword {},
    };
    let c = Hero {
        p: Wizard {},
        i: Wand {},
    };
    // this last one will err at compile time because it has not been enabled
    let d = Hero {
        p: Warrior {},
        i: Wand {},
    };
}
3 Likes

FYI, your code seems to behave the same even if you remove the stub functions - so I think you can get away with just having an empty trait :slight_smile:

This is a cool trick, though! I was slightly concerned that it might not work well if you had multiple potential combinations (e.g. what if you also had (Warrior, Staff) and tried to create a (Warrior, Wand), but in that case the error message was arguably more helpful:

error[E0277]: the trait bound `(Warrior, Wand): Wield<Warrior, Wand>` is not satisfied
  --> src/main.rs:53:13
   |
53 |     let d = Hero {
   |             ^^^^ the trait `Wield<Warrior, Wand>` is not implemented for `(Warrior, Wand)`
   |
   = help: the following implementations were found:
             <(Warrior, Staff) as Wield<Warrior, Staff>>
             <(Warrior, Sword) as Wield<Warrior, Sword>>
             <(Wizard, Sword) as Wield<Wizard, Sword>>
             <(Wizard, Wand) as Wield<Wizard, Wand>>
2 Likes

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.