Rust编程精华:从基础到实战技巧
1. Rust 与 Firefox 的关系
Rust 最早和 Mozilla 有很深的关系。Mozilla 参与推动 Rust 的发展,并且在 Firefox 中逐步使用 Rust 编写一些对性能和安全性要求较高的组件。
Rust 不是为了“写 Firefox”才出现的,但 Firefox 是 Rust 早期重要的实际应用场景之一。
Rust 的核心优势主要有三个:
1. 性能接近 C/C++
2. 内存安全
3. 无畏并发
所谓“无畏并发”,意思是 Rust 通过所有权、借用、生命周期等机制,在编译阶段尽量发现数据竞争问题,让程序员更安全地写多线程程序。
2. Rust 程序入口:main 函数
Rust 可执行程序从 main 函数开始运行。
fn main() {
println!("Hello, Rust!");
}
main 函数是一个特殊函数,是 Rust 可执行程序最先执行的代码。
3. Rust 代码风格
Rust 官方推荐使用 4 个空格缩进,而不是 Tab。
fn main() {
println!("hello");
}
这不是语法强制要求,但属于 Rust 社区的常见代码风格。
4. 使用 rustc 编译单个文件
最简单的 Rust 程序可以直接用 rustc 编译:
rustc main.rs
编译后会生成可执行文件。
在 Windows 上可能生成:
main.exe
main.pdb
其中:
.exe:真正的可执行程序
.pdb:调试信息文件,主要给调试器使用
.exe 文件运行时一般不需要安装 Rust。也就是说,别人电脑上没有 Rust 环境,也可以运行你编译好的 .exe 文件。
5. Cargo 是什么
Cargo 是 Rust 的项目管理工具,可以理解成:
Cargo = 构建工具 + 包管理器 + 项目管理工具
常用命令:
cargo build
编译项目。
cargo run
编译并运行项目。
cargo check
只检查代码能不能通过编译,不生成最终可执行文件,所以比 cargo build 更快。
cargo build --release
编译发布版本,会开启优化,程序运行更快,但编译时间更长。
注意你笔记里的 cargo bulid 拼错了,正确写法是:
cargo build
6. let:变量绑定
Rust 使用 let 声明变量。
let x = 5;
但是 Rust 中默认变量是不可变的。
let x = 5;
x = 6; // 错误
如果想让变量可以修改,需要加 mut。
let mut x = 5;
x = 6;
所以:
let:声明变量
mut:表示这个变量可以被修改
7. 常量和变量
Rust 中有变量,也有常量。
变量使用 let:
let x = 10;
let mut y = 20;
常量使用 const:
const MAX_POINTS: u32 = 100_000;
常量有几个特点:
1. 必须使用 const 声明
2. 必须写类型标注
3. 不允许使用 mut
4. 在程序运行期间值不能改变
5. 通常使用全大写字母命名
例如:
const PI: f64 = 3.1415926;
变量和常量的区别:
| 对比 | 变量 let | 常量 const |
|---|---|---|
| 是否可变 | 默认不可变,加 mut 可变 | 永远不可变 |
| 是否必须标注类型 | 通常不需要 | 必须 |
| 命名习惯 | 小写下划线 | 全大写下划线 |
| 使用场景 | 普通数据 | 固定配置、数学常量 |
8. Shadowing:隐藏
Rust 允许使用同名变量重新声明,这叫 shadowing,也叫“隐藏”。
let x = 5;
let x = x + 1;
let x = x * 2;
println!("{}", x); // 12
这里不是修改原来的 x,而是创建了一个新的 x,新的 x 把旧的 x 隐藏了。
Shadowing 和 mut 不一样。
mut 是修改同一个变量:
let mut x = 5;
x = 6;
Shadowing 是重新声明一个新变量:
let x = 5;
let x = 6;
Shadowing 的一个好处是可以改变变量类型:
let spaces = " ";
let spaces = spaces.len();
第一个 spaces 是字符串,第二个 spaces 是整数。
但是 mut 不能改变变量类型:
let mut spaces = " ";
spaces = spaces.len(); // 错误
9. match:模式匹配
match 是 Rust 中非常重要的控制流结构,类似 C/C++ 里的 switch,但更强大。
let number = 3;
match number {
1 => println!("one"),
2 => println!("two"),
3 => println!("three"),
_ => println!("other"),
}
_ 表示默认情况,类似 default。
match 的特点:
1. 必须穷尽所有可能情况
2. 可以匹配具体值
3. 可以匹配范围
4. 可以配合枚举使用
例如:
let x = 5;
match x {
1..=5 => println!("1 到 5"),
6..=10 => println!("6 到 10"),
_ => println!("其他"),
}
10. Rust 的数据类型
Rust 是静态类型语言,也就是说变量的类型在编译阶段就必须确定。
Rust 的数据类型主要分为两类:
1. 标量类型
2. 复合类型
一、标量类型
标量类型表示一个单独的值。
Rust 中主要有四种标量类型:
1. 整数类型
2. 浮点类型
3. 布尔类型
4. 字符类型
11. 整数类型
整数分为有符号整数和无符号整数。
有符号整数可以表示正数、负数和 0:
let a: i32 = -10;
无符号整数只能表示 0 和正数:
let b: u32 = 10;
其中:
i 表示 signed integer,有符号整数
u 表示 unsigned integer,无符号整数
常见整数类型:
| 类型 | 含义 |
|---|---|
| i8 | 8 位有符号整数 |
| i16 | 16 位有符号整数 |
| i32 | 32 位有符号整数 |
| i64 | 64 位有符号整数 |
| i128 | 128 位有符号整数 |
| isize | 根据平台决定大小 |
| u8 | 8 位无符号整数 |
| u16 | 16 位无符号整数 |
| u32 | 32 位无符号整数 |
| u64 | 64 位无符号整数 |
| u128 | 128 位无符号整数 |
| usize | 根据平台决定大小 |
默认整数类型是:
i32
例如:
let x = 10;
这里 x 默认是 i32。
12. isize 和 usize
isize 和 usize 的大小取决于计算机平台。
在 64 位系统上:
isize = i64
usize = u64
在 32 位系统上:
isize = i32
usize = u32
usize 经常用于集合索引,比如数组、Vec、字符串长度等。
let arr = [10, 20, 30];
let index: usize = 1;
println!("{}", arr[index]);
为什么索引用 usize?
因为索引本质上表示内存中某个位置的偏移量,它和机器地址大小有关,所以 Rust 使用 usize 表示集合索引。
13. 整数溢出
整数溢出指的是:一个整数超过了当前类型能表示的范围。
例如 u8 的范围是:
0 到 255
如果:
let x: u8 = 255;
let y = x + 1;
就会发生溢出。
Rust 对整数溢出的处理分两种情况:
调试模式
比如:
cargo build
cargo run
默认是调试模式。
调试模式下,Rust 会检查整数溢出,发现溢出后程序会 panic。
发布模式
比如:
cargo build --release
发布模式下,Rust 默认不检查普通整数运算溢出,而是进行环绕运算。
例如 u8:
255 + 1 会变成 0
也就是模 256 运算。
如果你想明确使用环绕运算,可以写:
let x = 255u8;
let y = x.wrapping_add(1);
14. 浮点类型
Rust 有两种浮点类型:
f32
f64
其中:
f32:单精度浮点数
f64:双精度浮点数
默认浮点类型是:
f64
例如:
let x = 3.14;
这里 x 默认是 f64。
如果想指定为 f32:
let x: f32 = 3.14;
一般情况下,Rust 默认使用 f64,因为现代 CPU 处理 f64 和 f32 的速度差别通常不大,而且 f64 精度更高。
15. 布尔类型
布尔类型只有两个值:
true
false
类型名是:
bool
例如:
let is_ok: bool = true;
let is_finished = false;
布尔值常用于条件判断:
if is_ok {
println!("成功");
} else {
println!("失败");
}
16. 字符类型
Rust 的字符类型是:
char
Rust 的 char 使用 Unicode,可以表示中文、英文、emoji 等字符。
let c = 'A';
let z = '中';
let emoji = '😀';
注意:
char 使用单引号
字符串使用双引号
例如:
let c = 'A'; // 字符
let s = "A"; // 字符串
Rust 的 char 不是简单的 1 字节,而是一个 Unicode 标量值,通常占 4 字节。
二、复合类型
复合类型可以把多个值组合成一个类型。
Rust 中基础的复合类型主要有:
1. 元组 Tuple
2. 数组 Array
17. Tuple:元组
元组可以把多个不同类型的值组合在一起。
let tup: (i32, f64, char) = (500, 6.4, 'A');
访问元组元素可以使用点号:
let x = tup.0;
let y = tup.1;
let z = tup.2;
也可以使用解构:
let (a, b, c) = tup;
元组的特点:
1. 长度固定
2. 元素类型可以不同
3. 可以通过 .0、.1、.2 访问
特殊的空元组 () 叫做 unit 类型,表示“没有有意义的值”。
let x = ();
18. 数组 Array
数组中所有元素必须是同一种类型,并且长度固定。
let arr = [1, 2, 3, 4, 5];
也可以显式标注类型:
let arr: [i32; 5] = [1, 2, 3, 4, 5];
含义是:
[i32; 5] 表示长度为 5 的 i32 数组
访问数组元素:
let first = arr[0];
let second = arr[1];
数组的特点:
1. 所有元素类型必须相同
2. 长度固定
3. 默认存储在栈上
4. 访问越界会导致运行时错误
例如:
let arr = [1, 2, 3];
println!("{}", arr[10]); // 越界
Rust 会在运行时检查数组访问是否越界。
19. 什么时候需要类型标注?
Rust 有类型推导能力,很多时候不需要手动写类型。
例如:
let x = 5;
let y = 3.14;
let flag = true;
Rust 可以自动推导出:
x: i32
y: f64
flag: bool
但是以下情况通常需要类型标注。
1)常量必须写类型
const MAX: u32 = 100;
2)类型无法推导时
例如字符串解析成数字:
let guess: u32 = "42".parse().expect("不是数字");
如果不写 u32,Rust 不知道你想把 "42" 解析成什么整数类型。
3)想主动指定类型时
let x: u8 = 100;
let y: f32 = 3.14;
4)函数参数和返回值通常要写类型
fn add(a: i32, b: i32) -> i32 {
a + b
}
Rust 不会像局部变量那样自动推导函数参数类型。
20. 默认类型总结
Rust 中常见默认类型:
| 数据 | 默认类型 |
|---|---|
| 整数字面量 | i32 |
| 浮点数字面量 | f64 |
| 布尔值 | bool |
| 字符 | char |
例如:
let a = 10; // i32
let b = 3.14; // f64
let c = true; // bool
let d = '中'; // char
21. 总结
1. Rust 默认变量不可变,想修改要加 mut。
2. let 可以进行 shadowing,重新绑定一个同名变量。
3. const 是真正的常量,必须写类型,不能 mut。
4. Rust 是静态类型语言,但有强大的类型推导。
5. 整数默认 i32,浮点默认 f64。
6. usize 常用于数组、Vec 等集合的索引。
7. 调试模式会检查整数溢出,发布模式默认进行环绕运算。
8. tuple 可以存不同类型,array 必须是同类型且长度固定。
9. cargo check 比 cargo build 快,适合开发时频繁检查。
10. cargo build --release 用于生成优化后的发布版本。
更多推荐

所有评论(0)