Expression 在“return”的末尾加上分号会有区别吗?
缔约国指出: 分号将任何表达式转换为语句,方法是丢弃其值并返回单位 在我做了一个实验之前,我以为我已经了解了这个概念:Expression 在“return”的末尾加上分号会有区别吗?,expression,rust,statements,Expression,Rust,Statements,缔约国指出: 分号将任何表达式转换为语句,方法是丢弃其值并返回单位 在我做了一个实验之前,我以为我已经了解了这个概念: fn print_number(x: i32, y: i32) -> i32 { if x + y > 20 { return x } x + y } 这很好。然后,我在返回行的末尾添加了一个分号(returnx;)。据我所知,这会将行转换为语句,返回单位数据类型() 尽管如此,最终结果是一样的。我不是100%肯定我说的话,但这有点道
fn print_number(x: i32, y: i32) -> i32 {
if x + y > 20 { return x }
x + y
}
这很好。然后,我在返回行的末尾添加了一个分号(returnx;
)。据我所知,这会将行转换为语句,返回单位数据类型()
尽管如此,最终结果是一样的。我不是100%肯定我说的话,但这有点道理 还有一个概念正在发挥作用:可达性分析。编译器知道
return
expression语句后面的内容是不可访问的。例如,如果我们编译此函数:
fn test() -> i32 {
return 1;
2
}
fn print_number(x: i32, y: i32) -> i32 {
if x + y > 20 {
return x;
()
} else {
x + y
}
}
我们得到以下警告:
警告:无法访问表达式
-->src/main.rs:3:5
|
3 | 2
| ^
|
编译器可以忽略<代码>的“真”分支,如果<<代码>表达式以“<代码>返回< /代码>表达式结束,在确定<代码>类型时,只考虑“false”分支,如果表达式。
您还可以在中看到此行为。发散函数是不正常返回的函数(例如,它们总是失败)。尝试将返回表达式替换为失败代码>宏(扩展为对发散函数的调用)。事实上,return
表达式也被认为是发散的;这是上述可达性分析的基础
但是,如果在return
语句之后有一个实际的()
表达式,您将得到一个错误。此功能:
fn test() -> i32 {
return 1;
2
}
fn print_number(x: i32, y: i32) -> i32 {
if x + y > 20 {
return x;
()
} else {
x + y
}
}
给出以下错误:
错误[E0308]:类型不匹配
-->src/main.rs:4:9
|
4 | ()
|^^应为i32,找到()
|
=注意:应为'i32'类型`
找到类型“%1”()`
最后,当出现分歧表达式(包括return
表达式)后接分号时,编译器对它们的处理似乎有所不同:语句仍在分歧。通常情况下,if
表达式中的每个分支都应具有相同的类型。如果某个分支的类型未指定,编译器将尝试查找单个公共类型:
fn print_number(x: int, y: int) {
let v = if x + y > 20 {
3 // this can be either 3u, 3i, 3u8 etc.
} else {
x + y // this is always int
};
println!("{}", v);
}
在此代码中,3
未指定,但else
分支强制其具有int
类型
这听起来很简单:有一个函数可以将两个或多个类型“统一”到公共类型中,否则它会在不可能的情况下给您一个错误。但是如果有一个失败怎么办代码>在分支中
fn print_number(x: int, y: int) {
let v = if x + y > 20 {
fail!("x + y too large") // ???
} else {
x + y // this is always int
};
println!("{}", v); // uh wait, what's the type of `v`?
}
我希望失败代码>不会影响其他分支,毕竟这是一个例外情况。由于这种模式在锈蚀中非常常见,因此引入了分叉型的概念。不存在正在发散的类型的值。(它也被称为“无人居住类型”或“无效类型”,具体取决于上下文。不要与“单位类型”混淆,后者具有单个值()
),因为发散类型自然是任何其他类型的子集,编译器得出结论认为v
的类型只是else
分支的类型,int
返回
表达式与失败没有区别代码>用于类型检查。它突然从当前的执行流中逃脱,就像fail代码>(谢天谢地,它不会终止任务)。但是,发散类型不会传播到下一个语句:
fn print_number(x: int, y: int) {
let v = if x + y > 20 {
return; // this is diverging
() // this is implied, even when you omit it
} else {
x + y // this is always int
};
println!("{}", v); // again, what's the type of `v`?
}
注意,唯一的分号语句x
相当于表达式x;()
。通常a;b
的类型与b
相同,因此x;()
只有在x
不发散时才具有()
类型,并且在x
发散时才发散。这就是为什么你原来的代码不起作用
添加这样的特殊情况很有诱惑力:
- 你为什么不把
x;()
当x
发散时发散
- 当无法推断其类型时,为什么不为每个未指定的整数文本假定
uint
?(注:这是过去的情况。)
- 在统一多个特征对象时,为什么不自动找到公共超特征
事实是,设计类型系统并不困难,但验证它要困难得多,我们希望确保Rust的类型系统是经得起未来考验且长期存在的。如果它确实有用并且被证明对我们的目的是“正确的”,那么其中一些可能会发生,但不会立即发生。如何不同?没有分号会有什么区别?没有分号,return
表达式和发散表达式似乎与任何表达式都是类型兼容的,而且即使后跟分号,它们似乎也保留了这一特殊特性。这正是他们在IRC频道告诉我的。基本上,它对最终结果没有影响(至少在我的例子中是这样),但添加分号可以让人安心:)我觉得我更了解如何返回,失败代码>,并且发散函数工作。无论在return x
中是否存在分号,我的代码示例都会编译。所以我想底线是:添加它不会有什么不同吗?我编辑了我的代码示例来进一步澄清我的问题。但是,返回x
和返回x代码>最后似乎做了同样的事情,我不知道在这种情况下使用分号有什么区别。我的编辑是否会更改您问题的上下文?(仍在试图理解那些关于生锈的低级概念)。@sargas:你的编辑完全改变了这种情况。之前,函数的尾部是一个if
表达式;现在,函数的尾部是x+y
expre