表达式与块
Riddle 中很多东西都是表达式。表达式会产生值,而语句主要用于执行动作。 理解表达式和块,是理解 Riddle 函数、控制流和所有权规则的基础。
表达式会产生值
下面这些都是表达式:
1
x + y
foo(1, 2)
(2, 3)
point.x
items[0]
n as i64
表达式可以出现在变量初始化、函数参数、返回值和更大的表达式中。
元组表达式用逗号区分于普通分组括号:(2, 3) 是二元组,(2,) 是单元素元组,(2) 仍只是整数 2,() 表示 unit。
匿名函数
匿名函数使用方括号形式 [参数 -> 体](例如 [x -> x + 1]),可以保存到变量或作为参数传递。它按用法推断捕获方式(共享引用、可变引用或按值),并通过 Fn / FnMut / FnOnce 静态能力参与类型检查;move [参数 -> 体] 按值捕获所有使用到的外部位置。完整的捕获规则、impl Fn* 参数与返回位置用法见闭包与迭代器。
分号会丢弃值
在 Riddle 中,分号表示“把这个表达式当成语句执行,并丢弃它的结果”。
fun main() {
let x = 1 + 2;
x + 1;
}
x + 1; 有分号,所以它的结果不会作为函数体返回值。
如果去掉分号,它就会成为块的尾表达式:
fun value() -> i32 {
let x = 1 + 2;
x + 1
}
这个函数返回 x + 1 的结果。
块也是表达式
块由 {} 包围,可以包含多条语句,也可以有一个尾表达式:
fun main() {
let value = {
let a = 1;
let b = 2;
a + b
};
print!("{}", value)
}
这里内部块的值是 a + b,因此 value 会绑定到这个结果。
块创建作用域
块不仅能产生值,也会创建新的作用域:
fun main() {
let outer = 1;
{
let inner = 2;
print!("{}", inner);
}
print!("{}", outer)
// inner 在这里不可用
}
作用域会影响变量何时失效,也会影响引用是否逃逸。后面的“引用与逃逸”会详细解释这一点。
常见表达式
数组字面量和索引用于固定长度数组:
fun first() -> i32 {
let values: [i32; 3] = [1, 2, 3];
let zeros: [i32; 3] = [0; 3];
values[0]
}
类型转换使用 as:
let wide = 1i32 as i64;
当前支持整数之间、整数与浮点数之间、浮点数之间、布尔值到整数、整数到布尔值、u8 到 char、整数到原始指针,以及原始指针之间的转换。不支持的组合会报告 E0012。
区间表达式
a..b 与 a..=b 分别构造半开区间 [a, b) 和闭区间 [a, b],最常用于 for 循环:
fun total() -> i32 {
let mut sum = 0;
for value in 1..=4 {
sum += value;
}
sum
}
a..b 脱糖为 std::ops::range(a, b),a..=b 脱糖为 std::ops::range_inclusive(a, b),两个函数都在 std::ops 中,但区间表达式本身不需要导入。区间是右结合的普通表达式,优先级低于 ||;开区间 a..、..b 和 .. 尚未实现。
复合赋值会读取左侧、执行对应运算,再写回左侧:
fun count() {
let mut n: i32 = 1;
n += 2;
n <<= 1;
}
unsafe 块也是表达式。它为原始指针解引用、原始指针索引、DST 布局转换和不安全函数调用提供显式上下文;类型、move 和借用检查仍然照常执行:
let value = unsafe {
1
};