我有一个看起来像这样的特征:
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
的实例。这很难看。例如,我手动必须提供正确的同步/发送特征实现:
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
。
无需在结构定义处对内部处理程序强制执行约束。你可以推迟它们,直到你为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) {
//...
}
}