3. Rust 语法
3.1 变量和可变性
- 声明变量使用
let
关键字,默认情况下变量是不可变的(Immutable) - 使用
mut
关键字声明可变变量(Mutable Variable)
常量和不可变变量有一些区别:
- 常量使用
const
修饰,且类型需要被标注 - 常量不能使用
mut
,因为常量永远不可变 - 常量可以在任何作用域中声明,包括全局作用域
- 常量只能被设置为常量表达式,不能是函数调用的结果或任何其他只能在运行时计算得到的值
const MAX_POINTS: u32 = 100_000;
Rust 命名规范
常量使用全大写字母,单词之间使用下划线分隔。函数名和变量名使用蛇形命名法(Snake Case)。
隐藏(Shadowing):可以声明一个与之前变量同名的新变量,新变量会覆盖之前的变量。
let x = 5;
let x = x + 1;
let x = x * 2;
隐藏与 mut
本质不同,隐藏会创建一个新变量,而 mut
会改变原变量的值。隐藏可以绑定到新的类型。
3.2 数据类型
Rust 是一种静态类型(Statically Typed)语言,编译器在编译时就必须知道所有变量的类型。
Rust 有两种数据类型:标量(Scalar)和复合(Compound)。
Rust 会执行类型推断(Type Inference),根据变量的值推断其类型。但如果有多种可能的类型,需要显式标注类型,否则编译器会报错,例如下面的代码不能通过编译:
fn main() {
let guess = "42".parse().expect("Not a number!");
println!("{}", guess);
}
Rust 的标量类型主要有四种:整数、浮点数、布尔值和字符。
整数类型
长度 | 有符号 | 无符号 |
---|---|---|
8-bit | i8 | u8 |
16-bit | i16 | u16 |
32-bit | i32 | u32 |
64-bit | i64 | u64 |
128-bit | i128 | u128 |
arch | isize | usize |
一般使用 usize
类型来表示索引,其大小取决于运行程序的计算机架构。
整数字面值有下面几种表示方法:
数字字面值 | 例子 |
---|---|
十进制 | 98_222 |
十六进制 | 0xff |
八进制 | 0o77 |
二进制 | 0b1111_0000 |
字节(u8 ) | b'A' |
在调试模式下,整数如果发生溢出则会抛出 panic,而在发布模式下则不会检查。
浮点数类型
Rust 有两种浮点数类型:f32
和 f64
,默认类型是 f64
。
let x = 2.0; // f64
let y: f32 = 3.0; // f32
布尔类型
布尔类型有两个值:true
和 false
。
字符类型
Rust 的 char
类型使用单引号,且占用 4 个字节。
3.3 复合类型
Rust 有两种复合类型:元组(Tuple)和 数组(Array)。
let tup: (i32, f64, u8) = (500, 6.4, 1);
let (x, y, z) = tup;
let five_hundred = tup.0;
let six_point_four = tup.1;
let one = tup.2;
数组和元组一样,长度都是固定的。
let a = [1, 2, 3, 4, 5];
let first = a[0];
let second = a[1];
如果你希望数据存放在栈上,且有固定大小,可以使用数组。如果数据大小不固定,可以使用 Vector
。
数组的类型是 [T; N]
,T
是元素的类型,N
是数组的长度。下面声明数组的类型:
let a: [i32; 5] = [1, 2, 3, 4, 5];
可以使用初始值缩略:
let a = [3; 5]; // 等价于 let a = [3, 3, 3, 3, 3];
数组使用 []
访问元素,如果访问越界会导致 panic,编译会尝试检查,但不一定能检查出问题。
3.4 函数
函数使用 fn
关键字声明,函数名使用蛇形命名法。
fn main() {
println!("Hello, world!");
}
函数的形参必须标注类型。
Rust 是一门基于表达式的语言,函数体的最后一个表达式会被隐式返回。函数的定义是语句,语句没有返回值,也不能被赋值到变量上。
函数可以标注返回值类型:
fn five() -> i32 {
5
}
Rust 支持单行注释和多行注释,与 C/C++ 和 JavaScript 语法一致。
// 单行注释
/*
多行注释
*/
3.5 控制流
if
表达式:
fn main() {
let number = 6;
if number % 4 == 0 {
println!("number is divisible by 4");
} else if number % 3 == 0 {
println!("number is divisible by 3");
} else if number % 2 == 0 {
println!("number is divisible by 2");
} else {
println!("number is not divisible by 4, 3, or 2");
}
}
建议使用 match
表达式替代复杂的 if-else
表达式。
if
是一个表达式,可以在 let
语句的右侧使用。if
的每个分支都必须返回相同类型的值。
let condition = true;
let number = if condition {
5
} else {
6
};
注意:if-else
分支的返回值必须是兼容的类型。
loop
循环是一个无限循环,可以使用 break
退出循环,break
后可以返回一个值。
let mut counter = 0;
let result = loop {
counter += 1;
if counter == 10 {
break counter * 2;
}
};
println!("The result is {}", result);
while
循环则与其他语言类似。
for
循环可以遍历一个集合中的每一个元素。
let a = [10, 20, 30, 40, 50];
for element in a.iter() {
println!("the value is: {}", element);
}
Range
类型可以生成一个范围:
for number in (1..4).rev() {
println!("{}!", number);
}
println!("LIFTOFF!!!");
Range
生成的范围不包含结束值,如果需要包含结束值,可以使用 ..=
。.rev()
可以反转范围。