Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

表达式与块

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右
3as左
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 里照常执行,它只放开那些显式标注为不安全的操作。