черты и связанные типы

Я пытаюсь реализовать что-то на Rust с чертами и связанными типами. Я не уверен, как сформулировать свой вопрос словами, поэтому добавлю фрагмент кода, который, надеюсь, проиллюстрирует то, что я пытаюсь сделать.

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) {}

Я ожидал, что приведенный выше код будет компилироваться, но это не так.

Вместо этого я получаю:

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

Что, AFAICT, означает, что компилятор не может определить, что переменная person имеет черту Person.

Я использую следующую версию rustc:

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

Я что-то упускаю?


person ynimous    schedule 06.07.2015    source источник


Ответы (2)


Вот исправление:

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

Тип Per в Directory может быть переопределен в реализациях признаков. Компилятор не знает, реализует ли Self::Per (который является переопределенным Per в реализации) признак Person, поэтому вы должны привязать его для реализации Person.

person eulerdisk    schedule 06.07.2015

Вот правильный код:

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> - это общий синтаксис. Определения связанных типов не используют общий синтаксис.

Используйте полное имя в целом; не сокращайте его Per, оставьте Person. Он всегда квалифицируется (Directory::Person, C::Person, & c.), поэтому нет двусмысленности. (Dir - общепризнанная короткая форма Directory, поэтому я думаю, что любой из них будет приемлемым. Однако я, вероятно, предпочел бы использовать Directory.)

Также нет необходимости указывать связанный тип Person на Catalog; Self::Dir::Person подойдет.

person Chris Morgan    schedule 06.07.2015