Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

traits and associated-types

I'm trying to implement something on Rust with traits and associated types. I'm not sure how to form my question with words, so I'll add a code snippet which will hopefully illustrate what I'm trying to do.

pub trait Person {}

pub trait Directory<P: Person> {
    type Per = P;
    fn get_person(&self) -> Self::Per;
}

pub trait Catalog {
    type Per : Person;
    type Dir : Directory<Self::Per>;

    fn get_directory(&self) -> Self::Dir;
}

fn do_something<C>(catalog: C) where C: Catalog {
    let directory : C::Dir = catalog.get_directory();

    // let person : C::Per = directory.get_person();
    // The code above fails with:
    //  error: mismatched types:
    //   expected `<C as Catalog>::Per`,
    //      found `<<C as Catalog>::Dir as Directory<<C as Catalog>::Per>>::Per`
    //   (expected trait `Catalog`,
    //       found trait `Directory`) [E0308]

    let person = directory.get_person();
    do_something_with_person(person);
}

fn do_something_with_person<P: Person>(p: P) {}

I would expect that the above code would compile, but it does not.

Instead, I get:

error: the trait `Person` is not implemented for the type `<<C as Catalog>::Dir as Directory<<C as Catalog>::Per>>::Per` [E0277]

Which, AFAICT, means that the compiler cannot determine that the person variable has the Person trait.

I'm using the following rustc version:

rustc 1.2.0-dev (a19ed8ad1 2015-06-18)

Am I missing something?

like image 559
ynimous Avatar asked Jan 09 '23 00:01

ynimous


2 Answers

Here there's the correction:

pub trait Directory<P: Person> {
    type Per : Person = P;
    fn get_person(&self) -> Self::Per;
}

The type Per in Directory can be redefined in trait implementations. The compiler doesn't know if Self::Per (which is the re-defined Per in the implementation) implements the trait Person, so you have to bound it to implement Person.

like image 76
eulerdisk Avatar answered Jan 10 '23 14:01

eulerdisk


Here’s the correct code:

pub trait Person {}

pub trait Directory {
    type Person: Person;
    fn get_person(&self) -> Self::Person;
}

pub trait Catalog {
    type Dir: Directory;

    fn get_directory(&self) -> Self::Dir;
}

fn do_something<C: Catalog>(catalog: C) {
    let directory = catalog.get_directory();
    let person = directory.get_person();
    do_something_with_person(person);
}

fn do_something_with_person<P: Person>(p: P) {}

<P: Person> is generics syntax. Associated type definitions do not use generic syntax.

Go for the full name in general; don’t abbreviate it Per, leave it Person. It’s always qualified (Directory::Person, C::Person, &c.), so there’s no ambiguity. (Dir is an acknowledged short form of Directory, so there I guess either would be acceptable. I’d probably tend to go with Directory, however.)

There’s no need to have the associated type Person specified on Catalog, either; Self::Dir::Person will do.

like image 30
Chris Morgan Avatar answered Jan 10 '23 15:01

Chris Morgan