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 是一门基于表达式的语言,函数体的最后一个表达式会被隐式返回。函数的定义是语句,语句没有返回值,也不能被赋值到变量上。
语句(Statement)是执行一些操作但不返回值的指令。表达式(Expression)会计算并产生一个值。
语句示例:
fn main() {
let y = 6; // 语句
}表达式示例:
fn main() {
let y = {
let x = 3;
x + 1 // 表达式,注意没有分号
};
println!("The value of y is: {}", y); // 输出: The value of y is: 4
}在上面的例子中,x + 1 是一个表达式,它的值是 4。如果在表达式的结尾加上分号,它就变成了语句,不会返回值。
函数可以标注返回值类型:
fn five() -> i32 {
5
}也可以使用 return 关键字提前返回:
fn plus_one(x: i32) -> i32 {
if x < 0 {
return 0;
}
x + 1
}Rust 支持单行注释和多行注释,与 C/C++ 和 JavaScript 语法一致。
// 单行注释
/*
多行注释
*/Rust 还支持文档注释,用于生成文档:
/// 这是一个文档注释
/// 它会出现在生成的文档中
fn documented_function() {
// 函数实现
}使用 //! 可以为包含它的项(通常是模块或 crate)添加文档:
//! 这是一个模块级别的文档注释
//! 用于描述整个模块的功能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() 可以反转范围。