Rust: When Empty Isn't Bottom

Rust: When Empty Isn’t Bottom

Rust: When Empty Isn’t Bottom Written by ettolrach, 2026-09-08. Rust:当“空类型”并非“底类型”时,作者:ettolrach,2026-09-08。

Rust recently added the empty type to the language (waffle, 2026). In Rust, it’s called ! or never. Although you could use ! for function return types before, you couldn’t use it for other type annotations. The pull request which enabled this for all type annotations was merged on 25th August 2026 and is scheduled to be added to Rust 1.100 as of the time of writing. Rust 最近在语言中加入了空类型(waffle, 2026)。在 Rust 中,它被称为 !never。虽然之前你可以在函数返回类型中使用 !,但无法将其用于其他类型注解。允许在所有类型注解中使用该特性的合并请求已于 2026 年 8 月 25 日合并,截至本文撰写时,计划将其加入 Rust 1.100 版本。

It mostly works as you may expect it would, but there’s an interesting detail which surprises most people who learn about it: expressions which have ! as part of their type don’t necessarily coerce to any other type. Also, the empty and bottom types are, in fact, not necessarily the same for any language, including Rust. 它在大多数情况下的表现正如你所预期的那样,但有一个有趣的细节会让大多数初学者感到惊讶:类型中包含 ! 的表达式并不一定会强制转换为任何其他类型。此外,空类型(empty type)和底类型(bottom type)在任何语言(包括 Rust)中实际上并不一定相同。

What’s the difference? We define the empty type to have no values and no constructors. We also have a function to go with it: fn absurd<A>(x: !) -> A. This function takes in any value whose type is ! and returns something of the type of your choosing (technically, it doesn’t return something since it’s never called because you can’t ever have something of type ! to call it with, but it makes much more conceptual sense to be able to construct ‘anything’). 它们有什么区别?我们定义空类型为没有任何值且没有任何构造函数的类型。我们还有一个配套函数:fn absurd<A>(x: !) -> A。该函数接收任何类型为 ! 的值,并返回你所选择类型的对象(从技术上讲,它不会返回任何东西,因为它永远不会被调用,因为你永远无法获得一个类型为 ! 的值来调用它,但在概念上,能够构造出“任何东西”更有意义)。

The bottom type also has no values and no constructors. However, instead of having this absurd function, we use subtyping and make it the subtype of all types. This means, thanks to how inheritance works, we can use it in place of anything else (remember that if the type A is a subtype of the type B, then we can use A whenever the code requires B). 底类型同样没有任何值和构造函数。然而,底类型不是通过这种 absurd 函数,而是通过子类型化(subtyping)机制,使其成为所有类型的子类型。这意味着,得益于继承的工作方式,我们可以在任何地方使用它来替代其他类型(记住,如果类型 A 是类型 B 的子类型,那么在代码需要 B 的地方,我们就可以使用 A)。

The reason why they’re different should be obvious: the empty type doesn’t need our language to have subtyping while the bottom type does! Rust doesn’t have subtyping for all types (it does for lifetimes, but that’s irrelevant here), so it cannot have the bottom type. But there’s another more practical difference between them. 它们之所以不同,原因显而易见:空类型不需要语言支持子类型化,而底类型则需要!Rust 并不支持所有类型的子类型化(虽然它支持生命周期的子类型化,但这与此处无关),因此它不可能拥有底类型。但它们之间还有一个更实际的区别。

How the two work in code

两者在代码中是如何工作的

First, we’ll use a language with proper subtyping: Scala. Scala calls the empty type (and since it’s got subtyping, the bottom type) Nothing. To make use of Nothing, we’ll throw an exception. Although a thrown exception can be caught, it won’t return a value to the current, local code, like how panic! works in Rust. Thus, it returns Nothing. 首先,我们使用一种具有完善子类型化机制的语言:Scala。Scala 将空类型(由于它有子类型化,因此也是底类型)称为 Nothing。为了使用 Nothing,我们抛出一个异常。虽然抛出的异常可以被捕获,但它不会像 Rust 中的 panic! 那样向当前的局部代码返回一个值。因此,它返回 Nothing

val f: Int => Nothing = (_) => throw Error()
val g: Int => Int = f
@main def main() = ()

Because of subtyping rules and a concept called variance, we don’t need to do anything special here and can just specify g to use f. 由于子类型化规则和一种称为“型变”(variance)的概念,我们在这里不需要做任何特殊处理,只需指定 g 使用 f 即可。

In Rust, similar to Scala, we’ll use panic! to not return any value (and thus returning the type !). Since Rust doesn’t have subtyping, we need to make use of the absurd function. Rust implicitly puts a call to absurd around any expression whose type is !; in fact, Rust doesn’t have an absurd function (I find it helpful to think that way, though), it simply implicitly typecasts an expression of type ! to any type as required. This implicit typecasting is called coercion in Rust. 在 Rust 中,与 Scala 类似,我们使用 panic! 来不返回任何值(从而返回类型 !)。由于 Rust 没有子类型化,我们需要利用 absurd 函数。Rust 会在任何类型为 ! 的表达式周围隐式调用 absurd;事实上,Rust 并没有一个真正的 absurd 函数(尽管我认为这样思考很有帮助),它只是根据需要将类型为 ! 的表达式隐式类型转换为任何类型。这种隐式类型转换在 Rust 中被称为强制转换(coercion)。

Importantly! If a type contains ! somewhere in it as part of a different type, such as the function type, then the coercion does not happen. That’s why the below code fails to typecheck: 重要提示!如果一个类型在某个地方包含了 ! 作为其他类型的一部分(例如函数类型),那么强制转换就不会发生。这就是为什么下面的代码无法通过类型检查:

fn f() -> fn(i32) -> ! { |_| panic!() }
fn g() -> fn(i32) -> i32 { f() }
fn main() { }

This outputs the following error: 这会输出以下错误:

error[E0308]: mismatched types
--> src/main.rs:6:5
|
5 | fn g() -> fn(i32) -> i32 {
| -------------- expected `fn(i32) -> i32` because of return type
6 | f()
| ^^^ expected `i32`, found `!`
| = note: expected fn pointer `fn(_) -> i32` found fn pointer `fn(_) -> !`

Unlike with the bottom type, we can only typecast from ! to any other type. We can’t do the same if it’s nested in a different type. So we need to write something like the below code instead: 与底类型不同,我们只能将 ! 强制转换为任何其他类型。如果它嵌套在不同的类型中,我们就无法做到这一点。因此,我们需要编写如下代码:

fn f() -> fn(i32) -> ! { |_| panic!() }
fn g() -> fn(i32) -> i32 {
    |x| {
        let never: ! = f()(x); // coerces here
        never
    }
}
fn main() { }

And this compiles without any errors (though you’ll get some unused warnings, so you’ll need #[allow(unused)])! Thus, Rust isn’t any less powerful than Scala. It’s just a bit more awkward to use. I hope you can see why there’s a real and practical difference between the two types and why I think it’s important to compare Rust’s ! to the empty type, not the bottom type. 这段代码可以编译通过且没有任何错误(尽管你会收到一些未使用的警告,所以你需要加上 #[allow(unused)])!因此,Rust 并不比 Scala 弱。它只是用起来稍微麻烦一点。我希望你能明白为什么这两种类型之间存在真实且实际的区别,以及为什么我认为将 Rust 的 ! 与空类型进行比较,而不是与底类型进行比较,是非常重要的。

Where does the misconception come from?

这种误解从何而来?

I suspect, as with many things, from Wikipedia! The page for Bottom type (Wikipedia contributors, 2026) says: “If a type system is sound, the bottom type is uninhabited and a term of bottom type represents a logical contradiction. In such systems, typically no distinction is drawn between the bottom type and the empty type, and the terms may be used interchangeably.” 我怀疑,和许多事情一样,这源于维基百科!关于“底类型”的页面(Wikipedia contributors, 2026)写道:“如果一个类型系统是可靠的,底类型就是无值的,底类型的项代表逻辑矛盾。在这样的系统中,通常不区分底类型和空类型,这两个术语可以互换使用。”

This is quite misleading because it looks like the only precondition of that statement is that the type system is sound. In fact, we also require the type system to have a subtyping relation, because that’s how the bottom type works, by definition. 这非常具有误导性,因为它看起来好像该陈述的唯一前提是类型系统是可靠的。事实上,我们还要求类型系统具有子类型关系,因为这就是底类型按定义工作的方式。

Now, to be fair, the page for Bottom type does mention subtyping throughout, so you could forgive it and say that it’s ‘obvious’ from surrounding context. But the page for Empty type (Wikipedia contributors, 2026) doesn’t mention ‘subtype’ anywhere and says: “If a type system contains an empty type, the bottom type must be uninhabited too, so no distinction is drawn between them and both are denoted (\bot).” 公平地说,底类型页面确实通篇提到了子类型化,所以你可以原谅它,并说从上下文来看这是“显而易见”的。但“空类型”页面(Wikipedia contributors, 2026)在任何地方都没有提到“子类型”,并写道:“如果一个类型系统包含空类型,底类型也必须是无值的,因此它们之间没有区别,两者都表示为 (\bot)。”

Again, it blue-links ‘bottom type’, so you could expect the reader to assume the language needs subtyping, too. But I don’t think that’s how most people read that sentence. This, I think, is what’s been misleading people. 同样,它将“底类型”设为蓝色链接,所以你可以期望读者假设该语言也需要子类型化。但我认为大多数人并不是这样阅读那句话的。我认为,这正是误导人们的原因。