首页 > 解决方案 > 如何在这个结构定义中避免 PhantomData?

问题描述

我有一个看起来像这样的特征:

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

auto trait 实现会有一个额外的where C: Send/Sync界限,这里不合适。

是否有替代 PhantomData 允许我对之间的关​​系进行编码FirstHandler<C>C以便 Rust 编译器很高兴并且我不需要更多unsafe代码?

我不是在寻找关联的类型。处理程序 trait 及其实现者是在库中定义的,具体类型C是在使用该库的应用程序中定义的,因此C处理程序的 trait 实现不能定义具体类型。

这种设计的想法是允许处理程序链累积C处理程序链中所需的所有特征界限,这样当我有handler第二个片段中所示的变量时,隐含的特征界限是C: ConnectionThatWorksWithFirstHandler + ConnectionThatWorksWithSecondHandler + ConnectionThatWorksWithFinalHandler.

标签: rusttraits

解决方案


无需在结构定义处对内部处理程序实施约束。您可以延迟它们,直到您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) {
        //...
    }
}

推荐阅读