У меня есть черта, которая выглядит примерно так:
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
.