Stabilizing Rust's never type
Stabilizing Rust’s never type
稳定 Rust 的 never 类型
A function’s return type is supposed to indicate the kind of data that it produces. Rust’s “never” type, which is denoted by an exclamation mark (”!”), is the type the language uses to mark a function that never returns and other places where a value can never occur. 函数的返回类型旨在表明它所产生的数据类型。Rust 的“never”类型(用感叹号“!”表示)是该语言用于标记从不返回的函数,以及其他永远不会产生值的地方的类型。
For a long time, the never type was used internally by the compiler, but was considered an unstable feature. On August 24, after more than two years of work, Rust-compiler-contributor “waffle” finally managed to stabilize the type. It took so long, in part, because it involved a small breaking change to previous Rust editions, which the compiler maintainers needed to ensure did not impact much real code. 长期以来,never 类型一直被编译器内部使用,但被视为一项不稳定功能。8 月 24 日,经过两年多的努力,Rust 编译器贡献者“waffle”终于成功地稳定了该类型。之所以耗时如此之久,部分原因是它涉及对先前 Rust 版本的一个微小的破坏性变更,编译器维护者需要确保这不会影响到太多的实际代码。
(Note: Rust also uses exclamation marks to indicate calls to macros. The way the syntax is constructed, a place where it is valid to use the never type is not a valid place to put a macro invocation and vice versa.) (注:Rust 也使用感叹号来表示宏调用。根据语法结构,可以使用 never 类型的地方不能放置宏调用,反之亦然。)
Why a never type? There are two reasons that Rust has a never type, one practical and one philosophical. The practical reason is that it allows for more efficient generic code. For example, consider the FromStr trait in the standard library, which is used for types that can be instantiated from a string:
为什么需要 never 类型?Rust 拥有 never 类型有两个原因,一个是实用性的,一个是哲学性的。实用性的原因是它允许编写更高效的泛型代码。例如,考虑标准库中的 FromStr 特征,它用于可以从字符串实例化的类型:
trait FromStr: Sized {
type Err;
fn from_str(s: &str) -> Result<Self, Self::Err>;
}
FromStr::from_str() either returns a converted result, or a custom error type. For example, attempting to convert “foo” into an integer will return a ParseIntError. But some types have an infallible conversion. For example, it is always possible to convert a string into a ByteString. That implementation of FromStr could set Err to be the never type. Then the compiler would know that the error branch of the returned Result is never present, and could optimize out all of the code that touches it or checks for it.
FromStr::from_str() 要么返回转换后的结果,要么返回自定义错误类型。例如,尝试将 “foo” 转换为整数会返回 ParseIntError。但有些类型具有“无错转换”(infallible conversion)。例如,将字符串转换为 ByteString 总是可能的。该 FromStr 的实现可以将 Err 设置为 never 类型。这样编译器就会知道返回的 Result 中的错误分支永远不会出现,并可以优化掉所有涉及或检查该分支的代码。
impl FromStr for ByteString {
type Err = !;
fn from_str(s: &str) -> Result<Self, !> { ... }
// Keeps the same generic interface,
// but generates code equivalent to:
// fn from_str(s: &str) -> Self { ... }
}
The philosophical reason involves correct type inference. In Rust, constructs such as if statements and while loops are expressions; their results can be assigned to a variable. The compiler needs a type to infer for the result of an infinite loop, if the programmer writes one. That shouldn’t come up often in real code, but it turns out to simplify type inference to be able to treat that case uniformly, rather than adding special rules to handle it.
哲学上的原因涉及正确的类型推断。在 Rust 中,if 语句和 while 循环等结构都是表达式;它们的结果可以赋值给变量。如果程序员编写了一个无限循环,编译器需要为其结果推断出一个类型。这种情况在实际代码中不常出现,但事实证明,能够统一处理这种情况可以简化类型推断,而不是添加特殊的规则来处理它。
In particular, the never type has a useful property for simplifying code: it automatically coerces to any other type. This sounds strange, but it is safe, since the never type represents the “result” of a computation that will never produce a value. So, anywhere that the code claims to have a value of the never type, the compiler knows that it can’t possibly reach that code, and therefore it’s safe to ignore it. This is a form of type-system-driven dead-code elimination. 特别地,never 类型有一个简化代码的有用属性:它会自动强制转换为任何其他类型。这听起来很奇怪,但它是安全的,因为 never 类型代表了一个永远不会产生值的计算的“结果”。因此,在代码声称拥有 never 类型值的任何地方,编译器都知道它不可能执行到该代码,因此忽略它是安全的。这是一种由类型系统驱动的死代码消除形式。
For both of these reasons, Rust programmers have wanted to be able to use the never type in stable versions of the language. Making that happen required resolving a particularly thorny corner case. 出于这两个原因,Rust 程序员一直希望能够在语言的稳定版本中使用 never 类型。要实现这一点,需要解决一个特别棘手的边界情况。
Never fallback
Never 回退
Because of the way that conversions from the never type to other types are implemented, the compiler can sometimes end up in a situation where it cannot naively infer the concrete type of an expression. Consider this example, which defines an anonymous function (using ||, which is like Python or LISP’s lambda) that never returns, and then calls it in a way that expects a concrete error type (using the ? operator):
由于从 never 类型到其他类型的转换方式,编译器有时会陷入无法简单地推断表达式具体类型的境地。考虑这个例子,它定义了一个从不返回的匿名函数(使用 ||,类似于 Python 或 LISP 的 lambda),然后以期望具体错误类型的方式调用它(使用 ? 运算符):
let function_that_never_returns = || { loop {} };
function_that_never_returns()?;
That infinite loop is given a type of ! which is then implicitly converted to whatever the function is supposed to return. But since the function is defined locally and not given an explicit type, the compiler does not have sufficient information to say what that type is. The problem could be fixed by giving the function an explicit return type:
那个无限循环被赋予了 ! 类型,然后隐式转换为函数应该返回的任何类型。但由于该函数是在本地定义的,且没有给出显式类型,编译器没有足够的信息来确定该类型是什么。这个问题可以通过给函数一个显式的返回类型来解决:
let function_that_never_returns = || -> Foo { loop {} };
Since such an annotation would only be required in cases where the function cannot return anything, however, it would be a bit pointless to require the programmer to assign a fictitious type to it. So, the compiler includes a special rule: if, after all other type inference has been done, there is still an ambiguous type that cannot be determined, just assume that it should be the designated fallback type. 然而,由于这种标注仅在函数无法返回任何内容的情况下才需要,要求程序员为其分配一个虚构的类型有点毫无意义。因此,编译器包含了一条特殊规则:如果执行完所有其他类型推断后,仍然存在无法确定的歧义类型,则直接假设它应该是指定的“回退类型”(fallback type)。
Prior to the 2024 edition of Rust, that fallback type was () (the unit type, which has exactly one possible value). In the 2024 edition, the fallback type was changed to ! itself, essentially canceling out the implicit conversion. In the compiler internals, the never type still gets converted to an unknown type and then falls back, but from the programmer’s perspective the behavior is identical to having the never type only undergo implicit conversion when required for the types to make sense.
在 Rust 2024 版本之前,该回退类型是 ()(单元类型,恰好只有一个可能的值)。在 2024 版本中,回退类型被更改为 ! 本身,本质上抵消了隐式转换。在编译器内部,never 类型仍然会被转换为未知类型然后回退,但从程序员的角度来看,其行为与“仅在类型需要时才对 never 类型进行隐式转换”是一样的。
That change of behavior was, technically, a breaking change. Type inference for some code could change, which could in turn cause compilation errors. That is the purpose of Rust’s edition system: allow breaking changes in the front-end design of the language without breaking older code or requiring the whole ecosystem to update at once. In this case, however, there were reasons to want the new behavior backported to old editions. 从技术上讲,这种行为的改变是一个破坏性变更。某些代码的类型推断可能会发生变化,进而导致编译错误。这就是 Rust 版本系统的目的:允许语言前端设计中的破坏性变更,而不会破坏旧代码或要求整个生态系统同时更新。然而,在这种情况下,有一些理由希望将新行为向后移植到旧版本中。
Never infallible
Never 无错
For many years, the standard library has had an Infallible type to work around the unstable nature of the never type. It served the same semantic purpose as the never type, but did not have any special compiler support.
多年来,标准库一直有一个 Infallible 类型,以绕过 never 类型的不稳定特性。它在语义上与 never 类型具有相同的目的,但没有特殊的编译器支持。