变量与可变性
let 创建绑定,默认不可变:重新赋值报 E0031。需要更新时写 let mut。
fun main() {
let answer = 42;
let mut count = 0;
count += 1;
println!("{} {}", answer, count);
}
可变性属于绑定,不属于值:同一个值可以先被不可变绑定持有,再被可变绑定持有。
类型标注与推断
可以写类型,也可以让编译器从初始化式推断。字面量没有期望类型时默认整数是 i32、浮点是 f64:
fun main() {
let age: i32 = 18;
let name: &str = "Riddle";
let ratio = 0.5;
println!("{} {} {}", age, name, ratio);
}
没有隐式数值提升:i32 不会自动变成 i64 或 f64,要转换就写 as,见数据类型。
遮蔽
再写一次 let 会创建新绑定,可以换类型:
fun main() {
let label = "hello";
let label = label.len();
println!("{}", label); // 5
}
被遮蔽的值不会提前析构,它一直活到作用域结束。给一个实现了 Drop 的类型就能看出来:
struct R { n: i32 }
impl std::ops::Drop for R {
fun drop(&mut self) {
println!("drop {}", self.n);
}
}
fun main() {
let r = R { n: 1 };
let r = R { n: 2 };
println!("end");
}
输出是 end、drop 2、drop 1。Rust 会在遮蔽那一行析构旧值,Riddle 不会。
解构绑定
let 后面是模式,可以直接拆开元组和结构体:
struct Point { x: i32, y: i32 }
fun main() {
let (a, b) = (1, 2);
let Point { x, y } = Point { x: 3, y: 4 };
let (_, second) = (10, 20);
println!("{}", a + b + x + y + second);
}
mut 写在单个绑定前面,不能修饰整条语句:let (mut count, step) = (0, 5); 合法,let mut (count, step) = ... 是语法错误。
普通 let 没有分支,模式必须覆盖该类型的全部取值,枚举变体和字面量这类可反驳模式报 E0057。需要处理「匹配失败」时用 let-else:
fun unwrap_or_zero(value: Option<i32>) -> i32 {
let Some(number) = value else {
return 0;
};
number
}
else 块必须发散(return、break、continue 或无限 loop),否则报 E0066。匹配成功后绑定在后面的代码里可用,变体载荷同样会绑到名字上;clue build 产出的可执行文件与 riddle run / riddle repl 在这件事上行为一致。
延迟初始化
绑定可以先声明、后赋值,编译器检查每条路径:
fun choose(flag: bool) -> i32 {
let value: i32;
if flag {
value = 10;
} else {
value = 20;
}
value
}
不可变绑定的首次赋值不需要 mut;第二次赋值报 E0031。某条路径上没有赋值就读取,报 E0059。
const
const 声明编译期常量,必须写类型并给初始化式:
const WIDTH: usize = 8;
const HEIGHT: usize = WIDTH / 2;
fun main() {
let grid: [i32; 32] = [0; WIDTH * HEIGHT];
println!("{}", grid[0]);
}
const 可以写在模块顶层,也可以写在 impl 块里,后者用 类型::名字 访问:
struct Scale { factor: i32 }
impl Scale {
const DEFAULT: i32 = 1;
}
fun main() {
println!("{}", Scale::DEFAULT);
}
初始化式能用的东西比想象中少:字面量、其它常量、二元运算、无语句的块、元组/数组/结构体字面量、一元运算、字段访问、as 和下标。函数调用、if、match、循环、赋值都不算常量表达式,报 E0060。
求值器按有符号 i128 建模,再按申明类型截断并查范围,所以下面这些都能折叠:
const N: i32 = -1; // ok
const F: f64 = 1.5; // ok,浮点常量不参与折叠,也不被拒绝
const S: &str = "hi"; // ok,同上
const C: char = 'x'; // ok
const B: bool = true; // ok
const A: usize = 2 - 3; // E0011:结果超出 usize 范围
常量值超出申明类型报 E0011。求值成功的非负整数常量可以用作数组长度和 const 泛型实参。