Как я могу избежать PhantomData в этом определении структуры? - PullRequest
0 голосов
/ 05 сентября 2018

У меня есть черта, которая выглядит примерно так:

trait Handler<C> {
    fn handle(&self, msg: &Message, connection: &mut C);
}

Экземпляры должны быть связаны, как если бы вы связывали связующее ПО для обработчиков HTTP:

let handler = FirstHandler {
     next: SecondHandler {
         next: FinalHandler {},
     },
};

Каждый тип обработчика может накладывать дополнительные ограничения на тип C:

trait ConnectionThatWorksWithFirstHandler {
    ...
}

struct FirstHandler<C: ConnectionThatWorksWithFirstHandler, H: Handler<C>> {
    next: H,
    _phantom: PhantomData<C>,
}

Как вы можете видеть здесь, мне нужно PhantomData<C>, чтобы избежать ошибки E0392 (parameter C is never used). Однако PhantomData семантически неверен, потому что обработчики не содержат экземпляры C. Это безобразно Например, я должен вручную предоставить правильные реализации черты Sync / Send:

unsafe impl<C: ConnectionThatWorksWithFirstHandler, H: Handler<C>> Send for Handler<C, H> where H: Send {}
unsafe impl<C: ConnectionThatWorksWithFirstHandler, H: Handler<C>> Sync for Handler<C, H> where H: Sync {}

Реализации автоматической черты будут иметь дополнительную границу where C: Send/Sync, которая здесь не подходит.

Есть ли альтернатива PhantomData, которая позволяет мне кодировать отношения между FirstHandler<C> и C, чтобы компилятор Rust был доволен, и мне не нужно больше кода unsafe?

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

Идея этого проекта состоит в том, чтобы позволить цепочке обработчиков накапливать все границы черт для C, которые требуются в цепочке обработчиков, так что когда у меня есть переменная handler, как показано во втором фрагменте, тогда подразумеваемая граница черты равна C: ConnectionThatWorksWithFirstHandler + ConnectionThatWorksWithSecondHandler + ConnectionThatWorksWithFinalHandler.

1 Ответ

0 голосов
/ 05 сентября 2018

Нет необходимости применять ограничения на внутренний обработчик при определении структуры. Вы можете отложить их, пока не реализуете черту Handler для FirstHandler.

trait Handler<C> {
    fn handle(&self, msg: &Message, connection: &mut C);
}

struct FirstHandler<H> {
    next: H
}

impl<C, H> Handler<C> for FirstHandler<H>
where
    H: Handler<C>,
    C: ConnectionThatWorksWithFirstHandler,
{
    fn handle(&self, msg: &Message, connection: &mut C) {
        //...
    }
}
...