Как потребовать, чтобы связанные типы из двух трейтов были одинаковыми?

У меня есть 2 следующие черты:

trait Filter {
    type Message;
    fn is_valid(&self, message: &Self::Message) -> bool;
}

trait Client {
    type Message;
    fn send(&self, message: &Self::Message) -> Result<(), Error>;
}

Я хотел бы, чтобы реализация Filter и Client использовала один и тот же тип Message.

struct ClientWithFilter<C: Client, F: Filter> {
    filter: F,
    client: C,
}

impl<C: Client, F: Filter> ClientWithFilter<C, F> {
    /// C::Message or F::Message???
    fn check_and_send(&self, message: &C::Message) -> Result<(), Error> {
        if self.filter.is_valid(message) {
            self.client.send(message)
        } else {
            Err(Error::MessageInvalid)
        }
    }
}

Это не компилируется:

if self.filter.is_valid(message) {
    |                   ^^^^^^^ expected client::Filter::Message, found client::Client::Message
    |
    = note: expected type `&<F as client::Filter>::Message`
               found type `&<C as client::Client>::Message`

Компилятор видит 2 разных типа, а я хотел бы иметь один. Как я могу правильно написать это на Rust?


person bm842    schedule 13.03.2019    source источник
comment
Это не компилируется: добавьте сообщение об ошибке, если оно не компилируется, чтобы мы могли легко воспроизвести вашу проблему..   -  person hellow    schedule 13.03.2019
comment
Не могли бы вы включить сообщение об ошибке? Мы не сможем вам помочь, если вы этого не сделаете.   -  person LogicalBranch    schedule 13.03.2019


Ответы (1)


Вам необходимо соответствующим образом ограничить параметры типа:

struct ClientWithFilter<C, F>
where
    C: Client,
    F: Filter<Message = C::Message>,
{
    filter: F,
    client: C,
}

impl<C, F> ClientWithFilter<C, F>
where
    C: Client,
    F: Filter<Message = C::Message>,
{
    fn check_and_send(&self, message: &C::Message) -> Result<(), Error> {
        if self.filter.is_valid(message) {
            self.client.send(message)
        } else {
            Err(Error::MessageInvalid)
        }
    }
}

игровая площадка

Я думаю, что избыточное дублирование ограничений на реализацию необходимо на данный момент. Я полагаю, что есть RFC, позволяющий наследовать ограничения из определения структуры.

person Sebastian Redl    schedule 13.03.2019
comment
Вы также можете написать <C: Client, F: Filter<Message = C::Message>> напрямую (мне кажется, что where здесь не добавляет читабельности). Я бы хотел еще where C::Message = F::Message, но это пока не работает: github.com/rust -lang/rust/issues/20041. - person Alexey Romanov; 13.03.2019
comment
Чтобы избежать дублирования границ трейтов, вы можете просто удалить границы трейтов из самой структуры и оставить их только в реализации. Если структура может быть создана только частными конструкторами, конечный результат будет таким же. Вам нужны только границы типажа в структуре, если вам нужно использовать связанные типы в определении структуры, но здесь это не так. - person Sven Marnach; 13.03.2019