为什么 Rust 允许返回类型错误的代码,但只允许尾随分号?

Why does Rust allow code with the wrong return type, but only with a trailing semicolon?

考虑以下 Rust 代码:

1
2
3
4
5
6
fn f() -> i32 {
    loop {
        println!("Infinite loop!");
    }
    println!("Unreachable");
}

尽管返回类型错误,它仍会编译(带有警告)并运行。
看起来编译器在最后一行返回类型为 () 是可以的,因为它检测到此代码不可访问。

但是,如果我们删除最后一个分号:

1
2
3
4
5
6
fn f() -> i32 {
    loop {
        println!("Infinite loop!");
    }
    println!("Unreachable")
}

然后代码不再编译,出现类型错误:

1
2
3
4
5
6
7
error[E0308]: mismatched types
  --> src/main.rs:14:5
   |
14 |     println!("Unreachable")
   |     ^^^^^^^^^^^^^^^^^^^^^^^ expected `i32`, found `()`
   |
   = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)

这是为什么?这两个代码片段中的返回类型是否相同,()

注意:我有兴趣了解为什么 Rust 编译器在这两个示例中的行为不同,即 Rust 编译器是如何实现的。从语言设计的angular来看,我并不是要问一个关于它"应该"如何表现的哲学问题(我知道这样的问题可能是题外话)。


第一个代码块中的返回类型实际上是 !(称为 never),因为您有一个永远不会退出的循环(所以 rust 会警告您说它无法访问)。完整的类型是:

fn f() -> !

我怀疑 ! 更像是 Rust 中的"底部"类型,而不是其他任何东西。在第二种情况下,由于 i32 和 () 在编译器进行"不可达性"分析之前不匹配,您的函数可能会在类型检查的早期阶段出错,就像在第一个示例中一样。

编辑:按照建议,这里是 rust 书的相关部分 https://doc.rust-lang.org/book/ch19-04-advanced-types.html#the-never-type-that-never -返回


(将 Sven 的第一条评论转化为答案)

Rust 编译器需要推断函数体的类型。在第一种情况下,没有返回表达式,显然编译器推断 !作为返回类型,因为无限循环,这是有道理的。在第二种情况下,有一个返回表达式,因此类型推断求解器使用它来推断类型,这也很有意义。

我不认为这是在语言参考中指定的,我认为这在任何方面都不重要 - 只需省略 unreachable 语句就可以了。