Rust 中不同功能的意义何在?

我已经阅读了几个关于 SO 的答案,并收集了这些用例:

  • 当函数 panic! s
  • 当函数中有无限循环时

但我仍然不清楚为什么我们需要这样定义函数:

fn func() -> ! {
    panic!("Error!");
}

如果它的工作方式与此相同(没有感叹号):

fn func() {
    panic!("Error!");
}

同时,为什么我们需要在无限循环的函数中使用 ! ?看起来这个签名并没有带来任何真实的使用信息。

stack overflow What is the point of diverging functions in Rust?
原文答案
author avatar

接受的答案

这些签名之间的主要区别归结为 ! 可以 coerce 转换为任何其他类型,因此 compatible 可以转换为任何其他类型(由于从未采用此代码路径,我们可以假设它是我们需要的任何类型)。当我们有多个可能的代码路径时,这一点很重要,例如 if-elsematch

例如,考虑以下(可能是人为的,但希望足够清晰)代码:

fn assert_positive(v: i32) -> u32 {
    match v.try_into() {
        Ok(v) => v,
        Err(_) => func(),
    }
}

func 声明返回 ! 时,此函数 compiles 成功。如果我们删除返回类型, func 将被声明为返回 () ,并且编译 breaks

error[E0308]: `match` arms have incompatible types
 --> src/main.rs:8:19
  |
6 | /     match v.try_into() {
7 | |         Ok(v) => v,
  | |                  - this is found to be of type `u32`
8 | |         Err(_) => func(),
  | |                   ^^^^^^ expected `u32`, found `()`
9 | |     }
  | |_____- `match` arms have incompatible types

您还可以将其与 definition for Result::unwrap 进行比较:

pub fn unwrap(self) -> T {
    match self {
        Ok(t) => t,
        Err(e) => unwrap_failed("called `Result::unwrap()` on an `Err` value", &e),
    }
}

这里是 unwrap_failed is returning ! ,因此它与 Ok 情况下返回的任何类型相统一。


答案:

作者头像

编译器知道,遵循不同表达式(W.R.T.评估顺序)的任何内容都是无法实现的。在确定局部变量是否初始化时,它可以使用此信息来避免虚假负面。

考虑以下示例:

use rand; // 0.8.4

fn main() {
    let foo;
    if rand::random::<bool>() {
        foo = "Hello, world!";
    } else {
        diverge();
    }
    println!("{foo}");
}

fn diverge() {
    panic!("Crash!");
}

我们声明一个变量 foo ,但我们仅在 if 表达式的一个分支中初始化它。这未能与以下错误进行编译:

error[E0381]: borrow of possibly-uninitialized variable: `foo`
  --> src/main.rs:10:15
   |
10 |     println!("{foo}");
   |               ^^^^^ use of possibly-uninitialized `foo`

但是,如果我们更改 diverge 函数的定义:

fn diverge() -> ! {
    panic!("Crash!");
}

然后代码成功编译。编译器知道,如果 else 分支被取下,它将永远不会到达 println! ,因为 diverge() 偏离。因此, else 分支不初始化 foo 不是错误。