如何在此结构定义中避免 PhantomData? [英] How can I avoid PhantomData in this struct definition?

查看:38
本文介绍了如何在此结构定义中避免 PhantomData?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a trait that looks something like this:

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

实例应该像链接 HTTP 处理程序的中间件一样链接:

Instances are supposed to be chained like you would chain middlewares for HTTP handlers:

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

每个处理程序类型都可以对 C 类型施加额外的约束:

Each handler type can impose additional constraints on the type C:

trait ConnectionThatWorksWithFirstHandler {
    ...
}

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

正如你在这里看到的,我需要一个 PhantomData 来避免错误 E0392(参数 C 从未被使用).然而,PhantomData 在语义上是错误的,因为处理程序没有保存 C 的实例.这是丑陋的.例如,我必须手动提供正确的同步/发送特征实现:

As you can see here, I need a PhantomData<C> to avoid error E0392 (parameter C is never used). However, PhantomData is semantically wrong because the handlers are not holding instances of C. This is ugly. For example, I manually have to provide the correct Sync/Send trait implementations:

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 绑定,这在这里不合适.

The auto trait implementations would have an additional where C: Send/Sync bound which is not appropriate here.

是否有 PhantomData 的替代方案允许我对 FirstHandlerC 之间的关系进行编码,以便 Rust 编译器很高兴并且我不需要更多不安全代码?

Is there an alternative to PhantomData that allows me to encode the relation between FirstHandler<C> and C such that the Rust compiler is happy and I don't need more unsafe code?

我不是在寻找关联类型. 处理程序特征及其实现者在库中定义,C 的具体类型在消费应用程序中定义库,因此具体类型 C 不能由处理程序的 trait 实现定义.

I'm not looking for associated types. The handler trait and its implementors are defined in a library, and the concrete type for C is defined in the application consuming the library, so the concrete type C cannot be defined by the handlers' trait implementations.

这种设计的想法是允许处理程序链累积处理程序链中所需的 C 的所有特征边界,以便当我拥有 handler 变量,如第二个片段所示,那么隐含的 trait bound 是 C: ConnectionThatWorksWithFirstHandler + ConnectionThatWorksWithSecondHandler + ConnectionThatWorksWithFinalHandler.

The idea with this design is to allow the chain of handlers to accumulate all the trait bounds for C that are required in the handler chain, so that when I have the handler variable as shown in the second snippet, then the implied trait bound is C: ConnectionThatWorksWithFirstHandler + ConnectionThatWorksWithSecondHandler + ConnectionThatWorksWithFinalHandler.

推荐答案

无需在结构体定义处对内部处理程序实施约束.您可以延迟它们,直到为 FirstHandler 实现 Handler 特性.

There is no need to enforce the constraints on the inner handler at the definition of the struct. You can delay them until you implement the Handler trait for 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) {
        //...
    }
}

这篇关于如何在此结构定义中避免 PhantomData?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆