简体   繁体   English

如何从枚举中删除不可能的变体?

[英]how to erase impossible variant from enum?

The setting: In a web server, the business code needs to access the request payload, which is located in either query or body, but not both, the deserialization of query is handled by a trait Q , and the deserialization of the body is handled by the trait B != Q , a middleware, f , (a piece of code that sitting between the web framework and business code) is responsible for the invocation of Q and B according to the request method, if it is a GET, then invoke Q, if it is a POST then invoke B, thus the signature of f must be like f<Query: Q, Body: B>(...) -> Payload<Query, Body> , "must be" because somewhere in the implementation of f :设置:在 web 服务器中,业务代码需要访问请求负载,该负载位于查询或正文中,但不能同时位于两者中,查询的反序列化由 trait Q处理,正文的反序列化处理通过 trait B != Q ,一个中间件f (位于 web 框架和业务代码之间的一段代码)负责根据请求方法调用 Q 和 B,如果是 GET,则调用 Q,如果是 POST 则调用 B,因此 f 的签名必须类似于f<Query: Q, Body: B>(...) -> Payload<Query, Body> ,“必须是”,因为某处在f的实施中:

let payload = if request_method == "GET" {
    let query: Query = ...::deserialize(request_query);
    Payload::Query(query)
} else if request_method == "POST" {
    let body: Body = ...::deserialize(request_body);
    Payload::Body(body)
} else {
    // error
}

...

payload

(sadly, I didn't find a way to tell the framework to call f<Q> or f<B> , nor a way to unify Q and B into a single trait) (遗憾的是,我没有找到告诉框架调用f<Q>f<B>的方法,也没有找到将 Q 和 B 统一为单一特征的方法)

In the business code, the handler for a GET would have the signature handler(Payload<Query, ()>)... and a POST would have handler(Payload<(), Body>)在业务代码中,GET 的处理程序将具有签名handler(Payload<Query, ()>)...而 POST 将具有handler(Payload<(), Body>)

The question: is there some technique to erase the unit type from the payload without turning it into a trait (trait is difficult to work with)?问题:是否有一些技术可以从有效负载中删除单元类型而不将其转换为特征(特征难以使用)? (The real rationale for erasing the unit type is actually something else, but let's just say I want the unit type erased) (擦除单位类型的真正理由实际上是另外一回事,但我们只是说我想擦除单位类型)

Edit: the meaning of "erasing" is to turn Payload<Query, Body> to Query or Body , ie handler(Payoad<(), Body>) would become handler(Body) - the "useless" unit variant of Payload<_, _> erased, resulting a enum with only one variant, essentially the "inner" itself.编辑:“擦除”的意思是将Payload<Query, Body>变成QueryBody ,即handler(Payoad<(), Body>)将成为handler(Body) - Payload<_ 的“无用”单元变体, _> 被删除,产生一个只有一个变体的枚举,本质上是“内部”本身。

Yes, there is a way to encode in Rust's type system that a variant of an enum can never be user.是的,有一种方法可以在 Rust 的类型系统中进行编码,即枚举的变体永远不能成为用户。 You have to use empty types (also called void types).您必须使用空类型(也称为 void 类型)。 One such type (two actually, but whatever) is already present in the prelude, it's the never type !一种这样的类型(实际上是两种,但无论如何)已经出现在前奏曲中,它是never类型! . . Since it's not completely clear about where you want to use it, I'll explain with the Result type.由于不完全清楚你想在哪里使用它,我将使用Result类型进行解释。

Assume you have a trait which requires returning a Result<T, U> where the implementer of the trait can choose which T and U .假设您有一个需要返回Result<T, U>的特征,该特征的实现者可以选择哪个TU For example, TryFrom :例如, TryFrom

struct A;
struct B;

impl TryFrom<A> for B {
    type Error = ...;
    fn try_from(a: A) -> Result<B, Self::Error> {
        Ok(B)
    }
}

In this example, it's clear that this conversion cannot fail.在此示例中,很明显此转换不会失败。 So what should I put in place of the dots?那么我应该用什么来代替这些点呢? Often people put the unit type () there, thinking that is carries no information (because there is a single value possible of that type) so it's perfect for the job.人们通常将单元类型()放在那里,认为它不携带任何信息(因为该类型可能存在单个值),因此它非常适合这项工作。 However, this is not exactly what we wanted, right?然而,这并不是我们想要的,对吧? There is actually a value of the type () , so this doesn't mean it's impossible to return Err(()) .实际上有一个()类型的值,所以这并不意味着不可能返回Err(()) What we want instead is a type of which there are no values, because in that case we are sure it's impossible to return a value of that type.相反,我们想要的是没有值的类型,因为在这种情况下,我们确定不可能返回该类型的值。 So the correct implementation would be所以正确的实现是

impl TryFrom<A> for B {
    type Error = !;
    ...
}

In this case, there is even a RFC that proposes the following code to Just Work:在这种情况下,甚至有一个 RFC 为 Just Work 提出以下代码:

let Ok(b) = B::try_from(A);

This would be accepted, because the compiler knows it cannot be an Err(something) , because then something: !这将被接受,因为编译器知道它不能是Err(something) ,因为然后something: ! which is impossible.这是不可能的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM