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 中很多东西都是表达式。表达式会产生值,而语句主要用于执行动作。 理解表达式和块,是理解 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;

当前支持整数之间、整数与浮点数之间、浮点数之间、布尔值到整数、整数到布尔值、u8char、整数到原始指针,以及原始指针之间的转换。不支持的组合会报告 E0012。

区间表达式

a..ba..=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
};