语句与表达式
在 Rust 的设计哲学中,一切皆表达式是一个核心概念。理解语句Statements 与 表达式Expressions的区别,是掌握 Rust 函数返回值、控制流赋值以及函数式编程风格的关键。
一、基本定义
1. 语句 (Statements)
语句是执行某种操作但不返回值的指令。
- 在 Rust 中,最常见的语句是变量声明
let x = 5;。 - 语句通常以分号
;结尾。 - 注意 :因为语句不返回值,所以你不能把
let语句赋值给另一个变量(例如let x = (let y = 5);会报错)。
2. 表达式 (Expressions)
表达式会计算并产生一个 值 。
- 数学运算(如
5 + 6)、函数调用、宏调用都是表达式。 - 甚至大括号包裹的代码块
{}也是表达式。 - 核心规则 :表达式的结尾 没有分号 。如果你在表达式末尾加上分号,它就会变成一条语句,返回值会变成单元类型
()。
二、块表达式 (Block Expressions)
在 Rust 中,我们可以使用 {} 创建一个作用域,这个作用域本身就是一个表达式,它的值是其中最后一行表达式的值。
fn main() {
let y = {
let x = 3;
x + 1 // 注意:这里没有分号!
};
println!("y 的值是: {y}"); // 输出 4
}
深度解析:
如果你在 x + 1 后面加了分号,变成 x + 1;,那么这个块就不再返回 4,而是返回 ()(unit 类型),编译器会因此报错(如果 y 预期是整数类型的话)。
三、函数中的应用:隐式返回
Rust 函数不需要显式写 return 关键字来返回值。只要函数体的最后一行是一个 表达式 (没有分号),该表达式的值就会自动作为函数的返回值。
fn add_one(x: i32) -> i32 {
x + 1 // 这是一个表达式,隐式返回其结果
}
fn main() {
let result = add_one(10);
println!("结果是: {result}");
}
显式 return 与隐式返回的区别:
- 隐式返回 (不带
return和分号):Rust 推荐的标准写法,代码更简洁。 - 显式 return :通常用于函数中途提前退出(提前返回)。
fn check_number(n: i32) -> String {
if n < 0 {
return String::from("错误:负数"); // 提前退出
}
String::from("正常") // 隐式返回
}
fn main() {
let result = check_number(-10);
println!("检查结果: {result}");
}
四、常见陷阱:分号的影响
分号在 Rust 中不仅仅是结束符,它是 类型的转换器 。它将一个“有值”的表达式转换成一个“无值”的语句。
| 示例 | 类别 | 结果/值 |
|---|---|---|
5 + 6 | 表达式 | 11 |
5 + 6; | 语句 | ()(Unit) |
let x = 5; | 语句 | 无值(报错不能被赋值) |
if true { 1 } else { 0 } | 表达式 | 1 |
五、综合示例:在一个函数中观察
下面的代码展示了如何在实际逻辑中混合使用语句和表达式:
fn main() {
let x = 5;
// 一个复杂的赋值表达式
let result = if x > 0 {
let temp = x * 2; // 语句
temp + 10 // 表达式:整个 if 块的值变为 20
} else {
0 // 表达式
};
println!("最终计算结果: {result}");
// 调用一个只有语句的函数
print_unit();
}
// 该函数没有返回值,或者说隐式返回 ()
fn print_unit() {
println!("我执行了一些操作,但我返回的是单元类型 ()");
// 这里其实隐藏了一个没有分号的 ()
}
六、操作符优先级
在Rust中,一切皆表达式,那么了解表达式的优先级就非常重要了,将Rust的操作符和表达式按优先级由高到低的顺序列了出来,具有相同优先级的操作符按相关性给定的顺序进行优先级计算。
总结对比
| 特性 | 语句 (Statements) | 表达式 (Expressions) |
|---|---|---|
| 是否有分号 | 是 (通常以 ;结尾) | 否 (末尾无 ;) |
| 是否有返回值 | 否 (返回 ()) | 是 |
| 典型例子 | let x = 5; | x + 5/my_func() |
| 函数末尾 | 不会作为返回值 | 会自动作为返回值 |