当前位置: 首页 > 知识库问答 >
问题:

Rust中的函数发散有什么意义?

潘弘扬
2023-03-14

我已经阅读了一些答案,并收集了以下用例:

  • 当一个函数慌了!s
  • 当一个函数有无限循环时

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

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

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

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

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


共有2个答案

锺博耘
2023-03-14

编译器知道任何跟随发散表达式(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`

但是,如果我们更改发散函数的定义,如下所示:

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

然后代码成功编译。编译器知道如果执行else分支,它将永远不会到达println 因为发散()发散。因此,else分支未初始化foo不是错误。

诸葛茂勋
2023-03-14

这些签名之间的主要区别归结为这样一个事实:可以强制为任何其他类型,因此与任何其他类型兼容(因为此代码路径从未被采用,所以我们可以假定它属于我们需要的任何类型)。当我们有多个可能的代码路径时,例如if-elsematch,这一点很重要。

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

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

func声明为返回,此函数编译成功。如果删除返回类型,func将声明为返回(),编译中断:

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

您还可以将其与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正在返回,因此它与在Ok大小写中返回的任何类型相统一。

 类似资料:
  • 通读Rust这本书,我遇到了一个有趣的话题——发散函数: Rust对“发散函数”有一些特殊的语法,这些函数不会返回: 发散函数可以用作任何类型: 发散函数的用例是什么?书上说 惊慌失措!()导致当前执行线程因给定消息而崩溃。因为这个函数会导致崩溃,它永远不会返回,所以它的类型是

  • 根据锈迹参考, 如果存在函数(snip),其返回类型必须是以下类型之一: >

  • Rust有二进制文字、二进制格式化程序和大量整数类型,但没有显式的二进制数字类型。 的确,在通用机器中,无符号整数的预期实现是一个大/小端二进制数。然而,这与高级语言的语法相去甚远。例如,如果我有一个八位二进制数,我想在语法上把它当作一个基本的数字类型来处理,我有两个问题:(1)数字的字符表示,和(2)数字的类型声明。如果我决定坚持使用,我必须添加一层字符串操作(在Rust中)或一层向量操作(例如

  • 问题内容: 所以我正在学习Java。我有一个月的时间,我刚刚了解了构造函数。但是我看不到创建一个的全部目的。为什么我什么时候要使用?我知道它没有主方法,您可以从主类中调用构造函数。任何人都可以启发我这个话题,这对我有很大帮助。 问题答案: 构造函数是用来初始化/设置类实例的对象。 如果您有一个对象需要一些处理才能使用(例如,初始化成员),则应在构造函数中执行此操作。 理想情况下,您永远不应拥有“部

  • 问题内容: 我想知道限制的含义是什么? 问题答案: 这是C99中引入的内容,它使编译器知道传入的指针与参数中的任何其他指针都没有指向相同的位置。如果向编译器提供此提示,则它可以进行一些更积极的优化而不会破坏代码。 例如,考虑以下功能: 显然,它从指针中添加了两个数字。如果需要,我们可以像这样使用它: 显然,它将输出8; 它本身增加了4。但是,如果我们添加到像这样: 那么以前的命令现在无效;它作为两

  • 问题内容: 我最近在查看Python 3.3语法规范时发现了一些有趣的东西: 在Python 2中缺少可选的“箭头”块,并且在Python 3中找不到有关其含义的任何信息。事实证明这是正确的Python,并已被解释器接受: 我认为这可能是某种前提语法,但是: 我无法x在此处进行测试,因为它仍未定义, 无论我在箭头(例如2 < 1)后面加上什么,它都不会影响功能行为。 熟悉此语法的任何人都可以解释吗