表达式与块
Riddle 里几乎一切都是表达式:字面量、路径、运算、调用、字段访问、if、match、loop、块都是,并且都能产生值。
1
x + y
foo(1, 2)
point.x
items[0]
n as i64
语句是丢弃了结果的表达式。加上分号就表示「求值然后丢掉」:
fun value() -> i32 {
let x = 1 + 2;
x + 1
}
x + 1 没有分号,它是块的尾表达式,也就是返回值;写成 x + 1; 就变成语句,函数返回 ()。
块
{ ... } 是表达式,同时创建一个作用域:
fun main() {
let outer = 1;
let inner_value = {
let inner = 2;
inner + outer
};
println!("{}", inner_value); // 3
// inner 在这里不存在
}
内层绑定遮蔽外层同名绑定,离开块后外层恢复可见。
运算符与优先级
从高到低:
| 优先级 | 运算符 | 结合性 |
|---|---|---|
| 1 | 后缀:调用 f()、方法 x.m()、字段 x.f、元组下标 x.0、索引 x[i]、? | 左 |
| 2 | 前缀:- ! * & &mut | 右 |
| 3 | as | 左 |
| 4 | * / % | 左 |
| 5 | + - | 左 |
| 6 | << >> | 左 |
| 7 | & | 左 |
| 8 | ^ | 左 |
| 9 | | | 左 |
| 10 | < <= > >= | 左 |
| 11 | == != | 左 |
| 12 | && | 左 |
| 13 | || | 左 |
| 14 | .. ..= | 左 |
| 15 | = 与复合赋值 | 右 |
几处容易和别的语言记混的地方:
- 移位低于加减:
1 << 2 + 1是1 << 3,等于 8; - 位运算高于比较:
1 & 2 == 0是(1 & 2) == 0,等于true; as高于算术和比较,但低于前缀运算符:-1i32 as u32先取负再转换,结果是4294967295;- 比较可以链式书写但类型检查会拒绝:
1 < 2 < 3报E0003,因为它等价于true < 3; - 区间两端都不能省,
0..、..5、..都是语法错误。
赋值也是表达式,值是 (),所以不能把它塞进需要具体类型的位置:
fun main() {
let mut a = 0;
let b: () = a = 1;
println!("{} {}", a, b); // 1 ()
}
复合赋值 +=、-=、*=、/=、%=、<<=、>>=、&=、^=、|= 先读左侧、运算、再写回。
区间
a..b 是左闭右开区间,a..=b 是闭区间,两者都是普通表达式值:
fun main() {
for i in 0..3 {
println!("{}", i); // 0 1 2
}
for i in 0..=2 {
println!("{}", i); // 0 1 2
}
}
a..b 脱糖为 std::ops::range(a, b),a..=b 脱糖为 std::ops::range_inclusive(a, b),不需要导入。开区间 a..、..b、.. 都没有实现。
类型转换
as 支持整数之间、整数与浮点数之间、浮点数之间、bool 与整数之间、u8 与 char 之间、整数到原始指针,以及原始指针之间。原始指针不能转回整数,其他组合报 E0012。
unsafe 块
unsafe { ... } 是表达式,为原始指针解引用与索引、不安全函数调用提供上下文,并保留自己的值:
fun main() {
let value = unsafe { 5 };
println!("{}", value);
}
类型检查、移动检查和借用检查在 unsafe 里照常执行,它只放开那些显式标注为不安全的操作。