Rust 语法详解 - 白话版
写给想学 Rust 的朋友,尽量用大白话把概念讲清楚,配合大量代码示例。
目录
- [为什么 Rust 这么难?](#为什么 Rust 这么难? "#%E4%B8%BA%E4%BB%80%E4%B9%88-rust-%E8%BF%99%E4%B9%88%E9%9A%BE")
- [安装与 Hello World](#安装与 Hello World "#%E5%AE%89%E8%A3%85%E4%B8%8E-hello-world")
- 变量与可变性
- 基本数据类型
- 函数
- 控制流
- [所有权 - Rust 的灵魂](#所有权 - Rust 的灵魂 "#%E6%89%80%E6%9C%89%E6%9D%83---rust-%E7%9A%84%E7%81%B5%E9%AD%82")
- 引用与借用
- 切片
- 结构体
- 枚举与模式匹配
- [Option 和 Result - 没有 Null 的世界](#Option 和 Result - 没有 Null 的世界 "#option-%E5%92%8C-result---%E6%B2%A1%E6%9C%89-null-%E7%9A%84%E4%B8%96%E7%95%8C")
- 错误处理
- 泛型
- [Trait - Rust 的接口](#Trait - Rust 的接口 "#trait---rust-%E7%9A%84%E6%8E%A5%E5%8F%A3")
- 生命周期
- 闭包
- 迭代器
- 模块系统
- 常用集合
- 智能指针
- 并发编程
- 常见陷阱与技巧
为什么 Rust 这么难?
Rust 难,主要难在这几点:
- 所有权系统 - 这是 Rust 独有的,其他语言没有
- 借用检查器 - 编译器会盯着你的引用,不让你乱来
- 生命周期 - 告诉编译器引用能活多久
但正是这些"难"的东西,让 Rust 写出来的程序没有空指针异常 、没有数据竞争 、内存安全。
安装与 Hello World
安装
bash
# Linux/macOS
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# 验证安装
rustc --version
cargo --version
Hello World
rust
// main.rs
fn main() {
println!("Hello, World!"); // println! 是宏,不是函数,所以有感叹号
}
bash
# 编译运行
rustc main.rs
./main
# 或者用 cargo(推荐)
cargo new hello_world
cd hello_world
cargo run
Cargo 常用命令
bash
cargo new project_name # 创建新项目
cargo build # 编译(debug 模式)
cargo build --release # 编译(release 模式,优化更多)
cargo run # 编译并运行
cargo check # 只检查错误,不生成二进制(快)
cargo test # 运行测试
cargo doc --open # 生成并打开文档
变量与可变性
默认不可变
Rust 的变量默认是不可变的,这是为了安全。
rust
fn main() {
let x = 5;
println!("x = {}", x);
// x = 6; // 错误!不能修改不可变变量
}
用 mut 让变量可变
rust
fn main() {
let mut x = 5; // 加上 mut 就可以改了
println!("x = {}", x);
x = 6; // 没问题
println!("x = {}", x);
}
变量遮蔽(Shadowing)
可以声明同名变量,新变量会遮蔽旧变量:
rust
fn main() {
let x = 5;
let x = x + 1; // 遮蔽,x 现在是 6
let x = x * 2; // 再遮蔽,x 现在是 12
println!("x = {}", x); // 输出 12
// 甚至可以改变类型!
let spaces = " "; // 字符串
let spaces = spaces.len(); // 变成数字,这在 mut 做不到
}
常量 vs 变量
rust
// 常量:必须注明类型,必须在编译时就知道值
const MAX_POINTS: u32 = 100_000; // 下划线增加可读性
fn main() {
// 常量不能遮蔽,不能被修改,不能在运行时计算
println!("Max points: {}", MAX_POINTS);
}
基本数据类型
整数类型
| 长度 | 有符号 | 无符号 |
|---|---|---|
| 8位 | i8 | u8 |
| 16位 | i16 | u16 |
| 32位 | i32 | u32 |
| 64位 | i64 | u64 |
| 128位 | i128 | u128 |
| 平台 | isize | usize |
rust
fn main() {
let a: i32 = -100; // 有符号 32 位,默认类型
let b: u8 = 255; // 无符号 8 位,0-255
let c = 98_222; // 下划线增加可读性
let d = 0xff; // 十六进制
let e = 0o77; // 八进制
let f = 0b1111_0000; // 二进制
// 整数溢出(debug 模式会 panic,release 会回绕)
// let overflow: u8 = 256; // 错误!u8 最大是 255
}
浮点类型
rust
fn main() {
let x = 2.0; // f64,默认类型,精度更高
let y: f32 = 3.0; // f32
// 浮点数运算
let sum = x + 5.0;
let diff = x - 1.0;
let product = x * 3.0;
let quotient = x / 2.0;
let remainder = x % 2.0;
}
布尔类型
rust
fn main() {
let t = true;
let f: bool = false;
// 常用于条件判断
if t {
println!("是真的!");
}
}
字符类型
rust
fn main() {
let c = 'z';
let z = 'ℤ';
let heart_eyed_cat = '😻';
let chinese = '中';
// char 是 Unicode 字符,占 4 字节
println!("{} {} {} {}", c, z, heart_eyed_cat, chinese);
}
元组
rust
fn main() {
// 创建元组
let tup: (i32, f64, u8) = (500, 6.4, 1);
// 解构
let (x, y, z) = tup;
println!("y = {}", y);
// 索引访问
let five_hundred = tup.0;
let six_point_four = tup.1;
let one = tup.2;
}
数组
rust
fn main() {
// 固定长度,栈上分配
let arr = [1, 2, 3, 4, 5];
// 指定类型和长度
let arr2: [i32; 5] = [1, 2, 3, 4, 5];
// 重复初始化:[3, 3, 3, 3, 3]
let arr3 = [3; 5];
// 访问元素
let first = arr[0];
let second = arr[1];
// 越界会 panic!
// let out_of_bounds = arr[100]; // 运行时错误
}
函数
基本语法
rust
fn main() {
say_hello();
greet("世界");
let sum = add(5, 3);
println!("5 + 3 = {}", sum);
}
// 无返回值
fn say_hello() {
println!("你好!");
}
// 有参数
fn greet(name: &str) {
println!("你好,{}!", name);
}
// 有返回值(注意没有分号的最后一行)
fn add(a: i32, b: i32) -> i32 {
a + b // 这是返回值,没有分号!
// 等价于: return a + b;
}
// 提前返回
fn abs(x: i32) -> i32 {
if x < 0 {
return -x; // 提前返回,有分号
}
x // 正常返回,无分号
}
语句 vs 表达式
这是 Rust 的一个重要概念:
rust
fn main() {
// 语句:执行动作,没有返回值
let x = 5; // 这是一个语句
// 表达式:有返回值
let y = {
let x = 3;
x + 1 // 表达式,没有分号,返回 4
};
println!("y = {}", y); // y = 4
// if 也是表达式!
let condition = true;
let number = if condition { 5 } else { 6 };
println!("number = {}", number);
}
控制流
if 表达式
rust
fn main() {
let number = 7;
// 基本 if
if number < 5 {
println!("小于 5");
} else if number < 10 {
println!("小于 10 但大于等于 5");
} else {
println!("大于等于 10");
}
// if 作为表达式(所有分支必须返回相同类型)
let result = if number % 2 == 0 {
"偶数"
} else {
"奇数"
};
println!("{} 是 {}", number, result);
}
loop 循环
rust
fn main() {
let mut count = 0;
// 无限循环
let result = loop {
count += 1;
if count == 10 {
break count * 2; // 可以返回值!
}
};
println!("结果: {}", result); // 20
// 循环标签(嵌套循环时很有用)
let mut remaining = 10;
'outer: loop {
println!("外层循环");
loop {
remaining -= 1;
if remaining == 5 {
break 'outer; // 直接跳出外层循环
}
}
}
println!("结束");
}
while 循环
rust
fn main() {
let mut number = 3;
while number != 0 {
println!("倒计时: {}!", number);
number -= 1;
}
println!("发射!");
// 遍历数组
let arr = [10, 20, 30, 40, 50];
let mut index = 0;
while index < 5 {
println!("arr[{}] = {}", index, arr[index]);
index += 1;
}
}
for 循环
rust
fn main() {
// 遍历数组(推荐方式)
let arr = [10, 20, 30, 40, 50];
for element in arr {
println!("元素: {}", element);
}
// 范围
for number in 1..4 { // 1, 2, 3(不含 4)
println!("{}", number);
}
for number in 1..=4 { // 1, 2, 3, 4(包含 4)
println!("{}", number);
}
// 倒计时
for number in (1..=5).rev() {
println!("倒计时: {}", number);
}
println!("发射!");
}
match 匹配
rust
fn main() {
let number = 7;
match number {
1 => println!("一"),
2 | 3 | 5 | 7 => println!("质数"),
4..=10 => println!("4 到 10 之间"),
_ => println!("其他数字"), // _ 是通配符
}
// match 是表达式
let result = match number {
1 => "一",
2 => "二",
_ => "其他",
};
println!("结果: {}", result);
}
if let
rust
fn main() {
let some_value = Some(7);
// 只关心一种情况时,比 match 简洁
if let Some(7) = some_value {
println!("是 7!");
}
// 等价于:
match some_value {
Some(7) => println!("是 7!"),
_ => (),
}
// 可以加 else
if let Some(x) = some_value {
println!("值是 {}", x);
} else {
println!("没有值");
}
}
所有权 - Rust 的灵魂
这是 Rust 最独特、最重要的概念。理解了所有权,就理解了 Rust 的一半。
三条规则
- Rust 中每个值都有一个所有者(owner)
- 同一时刻只能有一个所有者
- 当所有者离开作用域,值会被丢弃(drop)
变量作用域
rust
fn main() {
// s 不可用
{
let s = "hello"; // s 可用
// 使用 s
println!("{}", s);
} // s 离开作用域,被丢弃
// s 不可用
}
String 类型
rust
fn main() {
// 字符串字面量:不可变,编译时就确定
let s1 = "hello"; // &str 类型
// String 类型:可变,堆上分配
let mut s2 = String::from("hello");
s2.push_str(", world!"); // 可以修改
println!("{}", s2);
// 为什么 String 可以修改而 &str 不行?
// - &str 是编译时就确定的字符串切片,存在二进制文件中
// - String 是运行时在堆上分配的,可以动态改变
}
移动(Move)
rust
fn main() {
let s1 = String::from("hello");
let s2 = s1; // s1 的所有权移动到 s2
// println!("{}", s1); // 错误!s1 已经无效了
println!("{}", s2); // 正确
// 为什么?因为 String 包含堆上的数据
// 如果只是复制指针,两个变量指向同一块内存
// 离开作用域时会 double free!
// Rust 选择移动所有权,让 s1 失效
}
克隆(Clone)
rust
fn main() {
let s1 = String::from("hello");
let s2 = s1.clone(); // 深拷贝
println!("s1 = {}, s2 = {}", s1, s2); // 两个都有效
// clone 会复制堆上的数据,比较昂贵
// 但有时你需要这样做
}
栈上的数据:复制
rust
fn main() {
let x = 5;
let y = x; // 复制,不是移动
println!("x = {}, y = {}", x, y); // 两个都有效
// 为什么?因为整数存储在栈上,复制很便宜
// 实现了 Copy trait 的类型会复制而不是移动
// 包括:整数、浮点数、布尔值、字符、元组(如果元素都是 Copy)
}
函数与所有权
rust
fn main() {
let s = String::from("hello");
takes_ownership(s); // s 的所有权移动到函数
// println!("{}", s); // 错误!s 已经无效
let x = 5;
makes_copy(x); // x 是 Copy 类型,所以复制
println!("{}", x); // x 仍然有效
}
fn takes_ownership(some_string: String) {
println!("{}", some_string);
} // some_string 离开作用域,被 drop
fn makes_copy(some_integer: i32) {
println!("{}", some_integer);
} // some_integer 离开作用域,但什么也不会发生(Copy)
返回值与所有权
rust
fn main() {
let s1 = gives_ownership(); // 函数把所有权转移给 s1
let s2 = String::from("hello");
let s3 = takes_and_gives_back(s2); // s2 移动进函数,函数返回给 s3
// println!("{}", s2); // 错误!s2 已经无效
println!("{}", s3);
}
fn gives_ownership() -> String {
let some_string = String::from("hello");
some_string // 返回所有权给调用者
}
fn takes_and_gives_back(a_string: String) -> String {
a_string // 返回所有权
}
问题:太麻烦了!
rust
fn main() {
let s1 = String::from("hello");
let (s2, len) = calculate_length(s1); // 必须返回所有权...
println!("'{}' 的长度是 {}", s2, len);
}
fn calculate_length(s: String) -> (String, usize) {
let length = s.len();
(s, length) // 必须把 s 返回,否则调用者就用不了了
}
解决方案:引用!
引用与借用
什么是引用?
引用就像指针,但更安全。它让你借用值而不获取所有权。
rust
fn main() {
let s1 = String::from("hello");
let len = calculate_length(&s1); // 传递引用
println!("'{}' 的长度是 {}", s1, len); // s1 仍然有效!
}
fn calculate_length(s: &String) -> usize { // 借用
s.len()
} // s 离开作用域,但不会 drop,因为它不拥有值
可变引用
rust
fn main() {
let mut s = String::from("hello");
change(&mut s);
println!("{}", s); // hello, world
}
fn change(some_string: &mut String) {
some_string.push_str(", world");
}
引用的规则
规则一:要么一个可变引用,要么多个不可变引用
rust
fn main() {
let mut s = String::from("hello");
let r1 = &s; // 不可变引用
let r2 = &s; // 不可变引用
// let r3 = &mut s; // 错误!不能同时有可变和不可变引用
println!("{} {}", r1, r2);
// 但是可以这样:
let r3 = &mut s; // 现在可以了,因为 r1, r2 已经不用了
r3.push_str(" world");
}
规则二:引用必须始终有效(不能有悬垂引用)
rust
fn main() {
// let reference_to_nothing = dangle(); // 错误!
}
// 错误示例
fn dangle() -> &String { // 返回 String 的引用
let s = String::from("hello");
&s // 返回 s 的引用
} // s 被 drop 了!引用指向了无效内存!
// 正确做法:返回所有权
fn no_dangle() -> String {
let s = String::from("hello");
s // 返回 String,所有权转移
}
引用作用域
rust
fn main() {
let mut s = String::from("hello");
let r1 = &s;
let r2 = &s;
println!("{} {}", r1, r2);
// r1 和 r2 在这之后不再使用
let r3 = &mut s; // 现在可以了
r3.push_str(" world");
}
切片
切片是对集合的一部分的引用。
字符串切片
rust
fn main() {
let s = String::from("hello world");
let hello = &s[0..5]; // "hello"
let world = &s[6..11]; // "world"
println!("{} {}", hello, world);
// 简写
let hello = &s[..5]; // 从开头到索引 5
let world = &s[6..]; // 从索引 6 到结尾
let whole = &s[..]; // 整个字符串
// 注意:字符串切片索引是按字节,不是字符!
// 对于 UTF-8 字符串,要小心多字节字符
}
实际例子:找第一个单词
rust
fn first_word(s: &String) -> &str {
let bytes = s.as_bytes();
for (i, &item) in bytes.iter().enumerate() {
if item == b' ' {
return &s[0..i];
}
}
&s[..]
}
fn main() {
let mut s = String::from("hello world");
let word = first_word(&s); // word 是 &str
// s.clear(); // 错误!不能同时有可变和不可变引用
println!("第一个单词: {}", word);
}
数组切片
rust
fn main() {
let a = [1, 2, 3, 4, 5];
let slice = &a[1..3]; // [2, 3]
println!("{:?}", slice);
}
结构体
定义和实例化
rust
// 定义结构体
struct User {
username: String,
email: String,
sign_in_count: u64,
active: bool,
}
fn main() {
// 创建实例
let user1 = User {
email: String::from("user@example.com"),
username: String::from("user123"),
active: true,
sign_in_count: 1,
};
// 访问字段
println!("用户名: {}", user1.username);
// 可变实例
let mut user2 = User {
email: String::from("user2@example.com"),
username: String::from("user456"),
active: false,
sign_in_count: 0,
};
user2.active = true; // 修改字段
}
简写
rust
fn build_user(email: String, username: String) -> User {
User {
email, // 简写,变量名和字段名相同
username,
active: true,
sign_in_count: 1,
}
}
结构体更新语法
rust
fn main() {
let user1 = User {
email: String::from("user1@example.com"),
username: String::from("user1"),
active: true,
sign_in_count: 1,
};
// 从 user1 创建 user2,只修改部分字段
let user2 = User {
email: String::from("user2@example.com"),
..user1 // 其余字段从 user1 复制
};
// 注意:..user1 必须在最后
// 注意:username 被 String::from 移动了,所以 user1.username 不能再使用
// 但 active 和 sign_in_count 是 Copy 类型,所以 user1 还能用它们
}
元组结构体
rust
struct Color(i32, i32, i32);
struct Point(i32, i32, i32);
fn main() {
let black = Color(0, 0, 0);
let origin = Point(0, 0, 0);
// 它们是不同类型,不能混用
println!("黑色: ({}, {}, {})", black.0, black.1, black.2);
}
空结构体
rust
struct AlwaysEqual; // 没有任何字段
fn main() {
let subject = AlwaysEqual;
}
打印结构体
rust
#[derive(Debug)] // 添加这个属性
struct User {
username: String,
email: String,
}
fn main() {
let user = User {
username: String::from("user"),
email: String::from("user@example.com"),
};
println!("{:?}", user); // 调试格式
println!("{:#?}", user); // 美化的调试格式
}
方法
rust
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
// 关联函数(类似静态方法)
fn new(width: u32, height: u32) -> Self {
Self { width, height }
}
// 方法(第一个参数是 self)
fn area(&self) -> u32 {
self.width * self.height
}
fn width(&self) -> bool {
self.width > 0
}
// 可变方法
fn scale(&mut self, factor: u32) {
self.width *= factor;
self.height *= factor;
}
// 方法可以与字段同名
// 通常用于实现 getter
}
fn main() {
let rect = Rectangle::new(30, 50); // 调用关联函数
println!("rect: {:?}", rect);
println!("面积: {}", rect.area()); // 调用方法
println!("有宽度: {}", rect.width());
let mut rect2 = Rectangle::new(10, 20);
rect2.scale(2);
println!("放大后: {:?}", rect2);
}
枚举与模式匹配
定义枚举
rust
enum IpAddr {
V4,
V6,
}
fn main() {
let four = IpAddr::V4;
let six = IpAddr::V6;
route(four);
route(six);
}
fn route(ip_type: IpAddr) {
match ip_type {
IpAddr::V4 => println!("IPv4 地址"),
IpAddr::V6 => println!("IPv6 地址"),
}
}
枚举存储数据
rust
enum IpAddr {
V4(String),
V6(String),
}
fn main() {
let home = IpAddr::V4(String::from("127.0.0.1"));
let loopback = IpAddr::V6(String::from("::1"));
match home {
IpAddr::V4(ip) => println!("IPv4: {}", ip),
IpAddr::V6(ip) => println!("IPv6: {}", ip),
}
}
更复杂的枚举
rust
enum Message {
Quit, // 没有关联数据
Move { x: i32, y: i32 }, // 匿名结构体
Write(String), // 字符串
ChangeColor(i32, i32, i32), // 三个整数
}
impl Message {
fn call(&self) {
match self {
Message::Quit => println!("退出"),
Message::Move { x, y } => println!("移动到 ({}, {})", x, y),
Message::Write(text) => println!("写入: {}", text),
Message::ChangeColor(r, g, b) => println!("颜色: RGB({}, {}, {})", r, g, b),
}
}
}
fn main() {
let m = Message::Write(String::from("hello"));
m.call();
}
Option 枚举
Rust 没有 null,用 Option 表示可能不存在的值:
rust
enum Option<T> {
Some(T),
None,
}
fn main() {
let some_number = Some(5);
let some_char = Some('e');
let absent_number: Option<i32> = None;
// 必须处理 None 的情况
let x: Option<i32> = Some(5);
let sum = match x {
Some(n) => n + 1,
None => 0,
};
println!("sum = {}", sum);
}
match 必须穷尽所有可能
rust
fn main() {
let number = 7;
// match number {
// 1 => println!("一"),
// 2 => println!("二"),
// // 错误!没有处理其他情况
// }
// 正确:用 _ 处理其他情况
match number {
1 => println!("一"),
2 => println!("二"),
_ => println!("其他"),
}
}
绑定值的模式
rust
fn main() {
let x = Some(5);
let y = 10;
match x {
Some(50) => println!("是 50"),
Some(y) => println!("匹配到 y = {}", y), // 这里的 y 是新变量
_ => println!("默认情况"),
}
println!("外部的 y = {}", y); // y 还是 10
}
多重模式
rust
fn main() {
let x = 1;
match x {
1 | 2 => println!("一或二"),
3..=5 => println!("三到五"),
_ => println!("其他"),
}
}
解构结构体和枚举
rust
struct Point {
x: i32,
y: i32,
}
fn main() {
let p = Point { x: 0, y: 7 };
match p {
Point { x: 0, y } => println!("在 y 轴上,y = {}", y),
Point { x, y: 0 } => println!("在 x 轴上,x = {}", x),
Point { x, y } => println!("在 ({}, {})", x, y),
}
// 解构时重命名
match p {
Point { x: a, y: b } => println!("({}, {})", a, b),
}
// 忽略某些字段
match p {
Point { x, .. } => println!("x = {}", x),
}
}
匹配守卫
rust
fn main() {
let num = Some(4);
match num {
Some(x) if x < 5 => println!("小于 5: {}", x),
Some(x) => println!("大于等于 5: {}", x),
None => println!("没有值"),
}
}
绑定变量@
rust
fn main() {
let age = 25;
match age {
n @ 0..=12 => println!("儿童: {}", n),
n @ 13..=19 => println!("青少年: {}", n),
n @ 20..=30 => println!("青年: {}", n),
n => println!("其他年龄: {}", n),
}
}
Option 和 Result - 没有 Null 的世界
Option:可能没有值
rust
fn main() {
let some = Some(5);
let none: Option<i32> = None;
// 方法 1:match
let value = match some {
Some(v) => v,
None => 0,
};
// 方法 2:unwrap_or
let value = some.unwrap_or(0);
// 方法 3:unwrap(会 panic)
// let value = some.unwrap(); // 如果是 None 会 panic
// 方法 4:expect(自定义 panic 消息)
// let value = some.expect("应该有值");
// 方法 5:map
let doubled = some.map(|x| x * 2); // Some(10)
// 方法 6:and_then
let result = some.and_then(|x| {
if x > 3 {
Some(x * 2)
} else {
None
}
});
}
Result:可能出错
rust
enum Result<T, E> {
Ok(T),
Err(E),
}
use std::fs::File;
use std::io::ErrorKind;
fn main() {
let f = File::open("hello.txt");
let f = match f {
Ok(file) => file,
Err(error) => match error.kind() {
ErrorKind::NotFound => match File::create("hello.txt") {
Ok(fc) => fc,
Err(e) => panic!("创建文件失败: {:?}", e),
},
other_error => panic!("打开文件失败: {:?}", other_error),
},
};
}
unwrap 和 expect
rust
use std::fs::File;
fn main() {
// unwrap:成功返回值,失败 panic
let f = File::open("hello.txt").unwrap();
// expect:可以自定义错误消息
let f = File::open("hello.txt").expect("无法打开文件");
}
? 运算符
rust
use std::fs::File;
use std::io;
use std::io::Read;
// ? 会自动处理错误,把 Err 返回给调用者
fn read_username_from_file() -> Result<String, io::Error> {
let mut f = File::open("hello.txt")?; // 如果失败,返回 Err
let mut s = String::new();
f.read_to_string(&mut s)?; // 如果失败,返回 Err
Ok(s)
}
// 更简洁的写法
fn read_username_from_file2() -> Result<String, io::Error> {
let mut s = String::new();
File::open("hello.txt")?.read_to_string(&mut s)?;
Ok(s)
}
// 最简洁:使用标准库函数
fn read_username_from_file3() -> Result<String, io::Error> {
std::fs::read_to_string("hello.txt")
}
? 也可以用于 Option
rust
fn add_last(stack: &Vec<i32>) -> Option<i32> {
let last = stack.last()?; // 如果是 None,返回 None
let second_last = stack.get(stack.len() - 2)?; // 同上
Some(last + second_last)
}
错误处理
不可恢复错误:panic!
rust
fn main() {
// panic!("崩溃了!");
// 数组越界也会 panic
let v = vec![1, 2, 3];
// v[99]; // panic!
}
可恢复错误:Result
rust
use std::fs::File;
use std::io::ErrorKind;
fn main() {
let greeting_file_result = File::open("hello.txt");
let greeting_file = match greeting_file_result {
Ok(file) => file,
Err(error) => match error.kind() {
ErrorKind::NotFound => match File::create("hello.txt") {
Ok(fc) => fc,
Err(e) => panic!("创建文件失败: {:?}", e),
},
other_error => {
panic!("打开文件失败: {:?}", other_error);
}
},
};
}
使用闭包简化
rust
use std::fs::File;
use std::io::ErrorKind;
fn main() {
let greeting_file = File::open("hello.txt").unwrap_or_else(|error| {
if error.kind() == ErrorKind::NotFound {
File::create("hello.txt").unwrap_or_else(|error| {
panic!("创建文件失败: {:?}", error);
})
} else {
panic!("打开文件失败: {:?}", error);
}
});
}
泛型
泛型函数
rust
// 泛型函数:可以处理多种类型
fn largest<T: PartialOrd>(list: &[T]) -> &T {
let mut largest = &list[0];
for item in list {
if item > largest {
largest = item;
}
}
largest
}
fn main() {
let numbers = vec![34, 50, 12, 100, 65];
let result = largest(&numbers);
println!("最大的数字: {}", result);
let chars = vec!['y', 'm', 'a', 'q'];
let result = largest(&chars);
println!("最大的字符: {}", result);
}
泛型结构体
rust
// 泛型结构体
struct Point<T> {
x: T,
y: T,
}
// 多个泛型参数
struct Point2<T, U> {
x: T,
y: U,
}
fn main() {
let integer = Point { x: 5, y: 10 };
let float = Point { x: 1.0, y: 4.0 };
// let mixed = Point { x: 5, y: 4.0 }; // 错误!T 必须是同一类型
let mixed = Point2 { x: 5, y: 4.0 }; // 正确!
}
泛型方法
rust
struct Point<T> {
x: T,
y: T,
}
impl<T> Point<T> {
fn x(&self) -> &T {
&self.x
}
}
// 为特定类型实现方法
impl Point<f32> {
fn distance_from_origin(&self) -> f32 {
(self.x.powi(2) + self.y.powi(2)).sqrt()
}
}
fn main() {
let p = Point { x: 5, y: 10 };
println!("x = {}", p.x());
let p_float = Point { x: 3.0, y: 4.0 };
println!("距离原点: {}", p_float.distance_from_origin());
}
泛型枚举
rust
// 标准库中的 Option 和 Result 就是泛型枚举
enum Option<T> {
Some(T),
None,
}
enum Result<T, E> {
Ok(T),
Err(E),
}
// 自定义泛型枚举
enum Container<T> {
Single(T),
Pair(T, T),
Triple(T, T, T),
}
fn main() {
let single = Container::Single(1);
let pair = Container::Pair(1, 2);
let triple = Container::Triple(1, 2, 3);
}
Trait - Rust 的接口
Trait 类似其他语言的接口,但更强大。
定义和实现 Trait
rust
// 定义 Trait
pub trait Summary {
fn summarize(&self) -> String;
}
// 为类型实现 Trait
pub struct NewsArticle {
pub headline: String,
pub location: String,
pub author: String,
pub content: String,
}
impl Summary for NewsArticle {
fn summarize(&self) -> String {
format!("{}, by {} ({})", self.headline, self.author, self.location)
}
}
pub struct Tweet {
pub username: String,
pub content: String,
pub reply: bool,
pub retweet: bool,
}
impl Summary for Tweet {
fn summarize(&self) -> String {
format!("{}: {}", self.username, self.content)
}
}
fn main() {
let article = NewsArticle {
headline: String::from("Rust 发布新版本"),
location: String::from("北京"),
author: String::from("张三"),
content: String::from("Rust 1.70 发布了..."),
};
println!("{}", article.summarize());
let tweet = Tweet {
username: String::from("rustlover"),
content: String::from("Rust 太棒了!"),
reply: false,
retweet: false,
};
println!("{}", tweet.summarize());
}
默认实现
rust
pub trait Summary {
fn summarize(&self) -> String {
String::from("(阅读更多...)")
}
// 默认实现可以调用其他方法
fn summarize_author(&self) -> String;
fn summarize_with_author(&self) -> String {
format!("作者: {}。{}", self.summarize_author(), self.summarize())
}
}
pub struct NewsArticle {
pub headline: String,
pub author: String,
}
impl Summary for NewsArticle {
fn summarize_author(&self) -> String {
self.author.clone()
}
// 不实现 summarize,使用默认实现
}
fn main() {
let article = NewsArticle {
headline: String::from("新闻标题"),
author: String::from("张三"),
};
println!("{}", article.summarize()); // 默认实现
println!("{}", article.summarize_with_author()); // 调用默认方法
}
Trait 作为参数
rust
pub trait Summary {
fn summarize(&self) -> String;
}
// 方式 1:impl Trait 语法
pub fn notify(item: &impl Summary) {
println!("突发新闻!{}", item.summarize());
}
// 方式 2:Trait Bound 语法(更灵活)
pub fn notify2<T: Summary>(item: &T) {
println!("突发新闻!{}", item.summarize());
}
// 多个 Trait
pub fn notify3(item: &(impl Summary + std::fmt::Display)) {
println!("{}", item);
}
// 使用 where 子句(更清晰)
pub fn some_function<T, U>(t: &T, u: &U) -> i32
where
T: std::fmt::Display + Clone,
U: std::fmt::Debug + Clone,
{
// 函数体
0
}
返回 Trait
rust
pub trait Summary {
fn summarize(&self) -> String;
}
pub struct NewsArticle {
pub headline: String,
}
impl Summary for NewsArticle {
fn summarize(&self) -> String {
self.headline.clone()
}
}
// 返回实现了 Trait 的类型
fn returns_summarizable() -> impl Summary {
NewsArticle {
headline: String::from("新闻标题"),
}
}
// 注意:不能返回不同类型!
// fn returns_summarizable(switch: bool) -> impl Summary {
// if switch {
// NewsArticle { headline: String::from("新闻") }
// } else {
// Tweet { username: String::from("user"), content: String::from("内容") }
// }
// }
// 这会编译错误!
Trait Bound 条件
rust
use std::fmt::Display;
// 只有实现了 PartialOrd 和 Copy 的类型才能用这个函数
fn largest<T: PartialOrd + Copy>(list: &[T]) -> T {
let mut largest = list[0];
for &item in list {
if item > largest {
largest = item;
}
}
largest
}
// 使用 where 让代码更清晰
fn some_function<T, U>(t: &T, u: &U) -> i32
where
T: Display + Clone,
U: Clone + std::fmt::Debug,
{
0
}
常见 Trait
rust
// Debug:打印调试信息
#[derive(Debug)]
struct Point {
x: i32,
y: i32,
}
// Clone:深拷贝
#[derive(Clone)]
struct Data {
value: i32,
}
// Copy:栈上复制(隐式)
#[derive(Copy, Clone)]
struct Size {
width: u32,
height: u32,
}
// PartialEq:相等比较
#[derive(PartialEq)]
struct Person {
name: String,
age: u32,
}
fn main() {
let p1 = Point { x: 1, y: 2 };
println!("{:?}", p1); // Debug
let d1 = Data { value: 10 };
let d2 = d1.clone(); // Clone
let s1 = Size { width: 10, height: 20 };
let s2 = s1; // Copy(不需要 clone)
let p1 = Person { name: String::from("张三"), age: 25 };
let p2 = Person { name: String::from("张三"), age: 25 };
println!("{}", p1 == p2); // true
}
生命周期
生命周期是 Rust 最难的概念之一。它确保引用始终有效。
为什么需要生命周期?
rust
// 这个函数会报错!
fn longest(x: &str, y: &str) -> &str {
if x.len() > y.len() {
x
} else {
y
}
}
// 编译器不知道返回的引用是 x 还是 y
// 所以不知道返回值的生命周期
生命周期标注
rust
// 告诉编译器:返回值的生命周期与参数相同
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
fn main() {
let string1 = String::from("long string");
let string2 = String::from("xyz");
let result = longest(&string1, &string2);
println!("最长的字符串: {}", result);
// 这个例子会报错
let string3 = String::from("long string");
let result;
{
let string4 = String::from("xyz");
result = longest(&string3, &string4); // string4 活得不够长
}
// println!("{}", result); // 错误!string4 已经被释放
}
生命周期标注语法
rust
// 生命周期参数以 ' 开头,通常用 'a
fn function<'a>(x: &'a str) -> &'a str {
x
}
// 多个生命周期参数
fn function2<'a, 'b>(x: &'a str, y: &'b str) -> &'a str {
x // 返回 x,所以生命周期是 'a
}
// 结构体中的生命周期
struct ImportantExcerpt<'a> {
part: &'a str, // 引用必须比结构体活得长
}
fn main() {
let novel = String::from("Call me Ishmael. Some years ago...");
let first_sentence = novel.split('.').next().unwrap();
let excerpt = ImportantExcerpt {
part: first_sentence,
};
// excerpt 离开作用域后,novel 仍然有效
}
生命周期省略
有些情况下,编译器可以自动推断生命周期:
rust
// 省略前
fn first_word<'a>(s: &'a str) -> &'a str {
let bytes = s.as_bytes();
for (i, &item) in bytes.iter().enumerate() {
if item == b' ' {
return &s[0..i];
}
}
&s[..]
}
// 省略后(编译器自动推断)
fn first_word(s: &str) -> &str {
let bytes = s.as_bytes();
for (i, &item) in bytes.iter().enumerate() {
if item == b' ' {
return &s[0..i];
}
}
&s[..]
}
静态生命周期
rust
// 'static:整个程序运行期间都有效
let s: &'static str = "这是一个静态字符串";
// 字符串字面量都是 'static 的
// 但不要滥用 'static 来解决生命周期问题
方法的生命周期
rust
struct ImportantExcerpt<'a> {
part: &'a str,
}
impl<'a> ImportantExcerpt<'a> {
fn level(&self) -> i32 {
3
}
fn announce_and_return_part(&self, announcement: &str) -> &str {
println!("请注意: {}", announcement);
self.part
}
}
闭包
闭包是可以捕获环境的匿名函数。
基本语法
rust
fn main() {
// 基本闭包
let add = |a, b| a + b;
println!("1 + 2 = {}", add(1, 2));
// 带类型标注
let add_explicit = |a: i32, b: i32| -> i32 {
a + b
};
// 多行闭包
let calculate = |a, b| {
let sum = a + b;
let product = a * b;
(sum, product)
};
let (s, p) = calculate(3, 4);
println!("和: {}, 积: {}", s, p);
}
捕获环境
rust
fn main() {
let x = 4;
// 闭包可以捕获外部变量
let equal_to_x = |z| z == x;
let y = 4;
assert!(equal_to_x(y));
// 函数不能这样做!
// fn equal_to_x(z: i32) -> bool {
// z == x // 错误!函数不能捕获环境
// }
}
闭包的三种捕获方式
rust
fn main() {
// 1. FnOnce:获取所有权(只能调用一次)
let name = String::from("张三");
let consume = move || {
println!("再见,{}!", name); // name 被移动到闭包中
};
consume();
// println!("{}", name); // 错误!name 已经被移动
// 2. FnMut:可变借用
let mut list = vec![1, 2, 3];
let mut push_to_list = || {
list.push(4); // 可变借用
};
push_to_list();
println!("{:?}", list);
// 3. Fn:不可变借用
let list = vec![1, 2, 3];
let print_list = || {
println!("{:?}", list); // 不可变借用
};
print_list();
println!("{:?}", list); // list 仍然可用
}
闭包作为参数
rust
fn main() {
let list = vec![1, 2, 3, 4, 5];
// 使用闭包
let result: Vec<i32> = list.iter().map(|x| x * 2).collect();
println!("{:?}", result);
// 使用函数
fn double(x: &i32) -> i32 {
*x * 2
}
let result2: Vec<i32> = list.iter().map(double).collect();
println!("{:?}", result2);
}
迭代器适配器
rust
fn main() {
let numbers = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// map:转换
let doubled: Vec<i32> = numbers.iter().map(|x| x * 2).collect();
println!("翻倍: {:?}", doubled);
// filter:过滤
let evens: Vec<&i32> = numbers.iter().filter(|x| *x % 2 == 0).collect();
println!("偶数: {:?}", evens);
// fold:累积
let sum: i32 = numbers.iter().fold(0, |acc, x| acc + x);
println!("总和: {}", sum);
// 链式调用
let result: Vec<i32> = numbers
.iter()
.filter(|x| *x % 2 == 0) // 过滤偶数
.map(|x| x * 2) // 翻倍
.take(3) // 取前 3 个
.collect();
println!("结果: {:?}", result);
}
迭代器
创建迭代器
rust
fn main() {
let v = vec![1, 2, 3];
// iter():不可变引用
let iter1 = v.iter();
// iter_mut():可变引用
let mut v2 = vec![1, 2, 3];
let iter2 = v2.iter_mut();
// into_iter():获取所有权
let iter3 = v.into_iter();
}
Iterator trait
rust
pub trait Iterator {
type Item;
fn next(&mut self) -> Option<Self::Item>;
// 其他方法都有默认实现
}
fn main() {
let v = vec![1, 2, 3];
let mut iter = v.iter();
assert_eq!(iter.next(), Some(&1));
assert_eq!(iter.next(), Some(&2));
assert_eq!(iter.next(), Some(&3));
assert_eq!(iter.next(), None);
}
消费适配器
rust
fn main() {
let v = vec![1, 2, 3, 4, 5];
// sum:求和
let sum: i32 = v.iter().sum();
println!("总和: {}", sum);
// count:计数
let count = v.iter().count();
println!("数量: {}", count);
// any:是否有任意元素满足条件
let has_even = v.iter().any(|x| x % 2 == 0);
println!("有偶数: {}", has_even);
// all:是否所有元素都满足条件
let all_positive = v.iter().all(|x| *x > 0);
println!("都是正数: {}", all_positive);
// find:查找第一个满足条件的元素
let first_even = v.iter().find(|x| *x % 2 == 0);
println!("第一个偶数: {:?}", first_even);
// position:查找位置
let pos = v.iter().position(|x| *x == 3);
println!("3 的位置: {:?}", pos);
}
自定义迭代器
rust
struct Counter {
count: usize,
max: usize,
}
impl Counter {
fn new(max: usize) -> Counter {
Counter { count: 0, max }
}
}
impl Iterator for Counter {
type Item = usize;
fn next(&mut self) -> Option<Self::Item> {
if self.count < self.max {
self.count += 1;
Some(self.count)
} else {
None
}
}
}
fn main() {
let counter = Counter::new(5);
let sum: usize = counter.sum();
println!("总和: {}", sum); // 15
let counter2 = Counter::new(5);
let doubled: Vec<usize> = counter2.map(|x| x * 2).collect();
println!("翻倍: {:?}", doubled); // [2, 4, 6, 8, 10]
}
模块系统
模块基础
rust
// src/lib.rs 或 src/main.rs
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {
println!("添加到等待列表");
}
fn seat_at_table() {
println!("安排就座");
}
}
mod serving {
fn take_order() {
println!("点餐");
}
fn serve_order() {
println!("上菜");
}
}
}
pub fn eat_at_restaurant() {
// 绝对路径
crate::front_of_house::hosting::add_to_waitlist();
// 相对路径
front_of_house::hosting::add_to_waitlist();
}
use 关键字
rust
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
}
// 引入模块
use crate::front_of_house::hosting;
// 引入函数
use crate::front_of_house::hosting::add_to_waitlist;
// 引入多个项目
use std::collections::{HashMap, HashSet};
// 引入所有公共项目
use std::collections::*;
// 重命名
use std::fmt::Result as FmtResult;
fn main() {
hosting::add_to_waitlist();
add_to_waitlist();
}
文件分离
rust
// src/lib.rs
mod front_of_house; // 声明模块
pub use crate::front_of_house::hosting;
pub fn eat_at_restaurant() {
hosting::add_to_waitlist();
}
// src/front_of_house.rs(或 src/front_of_house/mod.rs)
pub mod hosting;
// src/front_of_house/hosting.rs(或 src/front_of_house/hosting/mod.rs)
pub fn add_to_waitlist() {}
访问权限
rust
mod outer {
pub fn public_function() {
println!("公开函数");
}
fn private_function() {
println!("私有函数");
}
pub mod inner {
pub fn inner_public() {
println!("内部公开函数");
}
fn inner_private() {
println!("内部私有函数");
}
}
}
fn main() {
outer::public_function();
// outer::private_function(); // 错误!私有
outer::inner::inner_public();
// outer::inner::inner_private(); // 错误!私有
}
常用集合
Vec 向量
rust
fn main() {
// 创建
let mut v: Vec<i32> = Vec::new();
let v2 = vec![1, 2, 3, 4, 5];
// 添加元素
v.push(1);
v.push(2);
v.push(3);
// 读取元素
let third = &v[2]; // 可能 panic
let third = v.get(2); // 返回 Option<&T>
// 遍历
for i in &v {
println!("{}", i);
}
// 可变遍历
for i in &mut v {
*i += 50;
}
// 存储多种类型
enum SpreadsheetCell {
Int(i32),
Float(f64),
Text(String),
}
let row = vec![
SpreadsheetCell::Int(3),
SpreadsheetCell::Text(String::from("blue")),
SpreadsheetCell::Float(10.12),
];
}
String 字符串
rust
fn main() {
// 创建
let mut s = String::new();
let s2 = "initial contents".to_string();
let s3 = String::from("initial contents");
// 追加
s.push_str("hello");
s.push('!'); // 单个字符
// 拼接
let s1 = String::from("Hello, ");
let s2 = String::from("world!");
let s3 = s1 + &s2; // s1 被移动,s2 被借用
// format! 宏
let s1 = String::from("tic");
let s2 = String::from("tac");
let s3 = String::from("toe");
let s = format!("{}-{}-{}", s1, s2, s3);
// 索引(不支持!)
// let c = s[0]; // 错误!
// 切片(小心 UTF-8)
let hello = "你好";
// let s = &hello[0..1]; // 可能 panic!中文是多字节
let s = &hello[0..3]; // 正确,一个中文字符占 3 字节
// 遍历
for c in "你好".chars() {
println!("{}", c); // 你,好
}
for b in "你好".bytes() {
println!("{}", b); // 每个字节
}
}
HashMap<K, V> 哈希表
rust
use std::collections::HashMap;
fn main() {
// 创建
let mut scores = HashMap::new();
// 插入
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Red"), 50);
// 访问
let team_name = String::from("Blue");
let score = scores.get(&team_name); // Option<&V>
// 遍历
for (key, value) in &scores {
println!("{}: {}", key, value);
}
// 更新
scores.insert(String::from("Blue"), 25); // 覆盖
// 只在键不存在时插入
scores.entry(String::from("Yellow")).or_insert(50);
scores.entry(String::from("Blue")).or_insert(50); // 不会覆盖
// 根据旧值更新
let text = "hello world wonderful world";
let mut map = HashMap::new();
for word in text.split_whitespace() {
let count = map.entry(word).or_insert(0);
*count += 1;
}
println!("{:?}", map);
}
智能指针
Box 堆分配
rust
fn main() {
// 在堆上分配
let b = Box::new(5);
println!("b = {}", b);
// 递归类型
enum List {
Cons(i32, Box<List>),
Nil,
}
use List::{Cons, Nil};
let list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));
}
Rc 引用计数
rust
use std::rc::Rc;
fn main() {
// 多所有权
let a = Rc::new(5);
let b = Rc::clone(&a); // 增加引用计数
let c = Rc::clone(&a); // 再增加
println!("引用计数: {}", Rc::strong_count(&a)); // 3
// 当所有引用离开作用域,数据才会被释放
}
RefCell 内部可变性
rust
use std::cell::RefCell;
fn main() {
// 在运行时检查借用规则,而不是编译时
let x = RefCell::new(5);
// 可变借用
*x.borrow_mut() += 1;
// 不可变借用
println!("x = {}", *x.borrow());
// 注意:运行时违反规则会 panic
// let ref1 = x.borrow();
// let ref2 = x.borrow_mut(); // panic!同时有可变和不可变借用
}
组合使用
rust
use std::cell::RefCell;
use std::rc::Rc;
#[derive(Debug)]
struct Node {
value: i32,
children: RefCell<Vec<Rc<Node>>>,
}
fn main() {
let leaf = Rc::new(Node {
value: 3,
children: RefCell::new(vec![]),
});
let branch = Rc::new(Node {
value: 5,
children: RefCell::new(vec![Rc::clone(&leaf)]),
});
println!("branch: {:?}", branch);
}
并发编程
线程
rust
use std::thread;
use std::time::Duration;
fn main() {
// 创建线程
let handle = thread::spawn(|| {
for i in 1..10 {
println!("子线程: {}", i);
thread::sleep(Duration::from_millis(1));
}
});
for i in 1..5 {
println!("主线程: {}", i);
thread::sleep(Duration::from_millis(1));
}
// 等待线程结束
handle.join().unwrap();
}
move 闭包
rust
use std::thread;
fn main() {
let v = vec![1, 2, 3];
// move 把所有权转移到线程中
let handle = thread::spawn(move || {
println!("向量: {:?}", v);
});
// println!("{:?}", v); // 错误!v 已经被移动
handle.join().unwrap();
}
消息传递
rust
use std::sync::mpsc;
use std::thread;
fn main() {
// 创建通道
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
let val = String::from("你好");
tx.send(val).unwrap();
// println!("{}", val); // 错误!val 已经被移动
});
// 接收消息(阻塞)
let received = rx.recv().unwrap();
println!("收到: {}", received);
// 多个发送者
let (tx, rx) = mpsc::channel();
let tx1 = tx.clone();
thread::spawn(move || {
let vals = vec![
String::from("你好"),
String::from("来自"),
String::from("线程"),
];
for val in vals {
tx1.send(val).unwrap();
}
});
thread::spawn(move || {
let vals = vec![
String::from("更多"),
String::from("消息"),
];
for val in vals {
tx.send(val).unwrap();
}
});
// 迭代接收
for received in rx {
println!("收到: {}", received);
}
}
共享状态
rust
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
// Mutex:互斥锁
let counter = Arc::new(Mutex::new(0));
let mut handles = vec![];
for _ in 0..10 {
let counter = Arc::clone(&counter);
let handle = thread::spawn(move || {
let mut num = counter.lock().unwrap();
*num += 1;
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
println!("结果: {}", *counter.lock().unwrap());
}
常见陷阱与技巧
1. 所有权陷阱
rust
fn main() {
let v = vec![1, 2, 3];
let v2 = v; // 移动
// println!("{:?}", v); // 错误!v 已经无效
// 解决方案 1:克隆
let v3 = vec![1, 2, 3];
let v4 = v3.clone();
println!("{:?} {:?}", v3, v4); // 都有效
// 解决方案 2:引用
let v5 = vec![1, 2, 3];
let v6 = &v5;
println!("{:?} {:?}", v5, v6); // 都有效
}
2. 借用检查器陷阱
rust
fn main() {
let mut v = vec![1, 2, 3];
// 错误示例
// let first = &v[0]; // 不可变借用
// v.push(4); // 可变借用
// println!("{}", first); // 错误!同时有可变和不可变借用
// 正确做法
let first = v[0];
v.push(4);
println!("{}", first);
}
3. 生命周期陷阱
rust
// 错误示例
// fn longest(x: &str, y: &str) -> &str {
// let result = String::from("really long string");
// result.as_str() // 错误!result 在函数结束时被释放
// }
// 正确做法
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
4. 字符串陷阱
rust
fn main() {
let s = "你好世界";
// 错误:中文字符占 3 字节
// let c = &s[0..1]; // panic!
// 正确做法
let c = &s[0..3]; // "你"
println!("{}", c);
// 更好的做法:使用 chars()
for c in s.chars() {
println!("{}", c);
}
}
5. 整数溢出
rust
fn main() {
// debug 模式会 panic,release 模式会回绕
let mut x: u8 = 255;
// x += 1; // debug: panic, release: 0
// 显式处理
x = x.checked_add(1).unwrap_or(0); // 溢出返回 None
x = x.saturating_add(1); // 溢出保持最大值
x = x.wrapping_add(1); // 溢出回绕
}
6. 使用 Option 和 Result
rust
fn main() {
// 不要用 unwrap(),用 ? 或 match
// let x = some_option.unwrap(); // 可能 panic
// 更好的做法
let x = match some_option {
Some(v) => v,
None => return,
};
// 或者
let x = some_option.ok_or("错误")?;
}
7. 避免克隆
rust
fn main() {
// 不要过度使用克隆
let s1 = String::from("hello");
let s2 = s1.clone(); // 有时是必要的,但考虑是否可以用引用
// 使用引用
fn print_string(s: &str) {
println!("{}", s);
}
print_string(&s1); // 不需要克隆
}
8. 使用迭代器代替循环
rust
fn main() {
let numbers = vec![1, 2, 3, 4, 5];
// 不推荐
let mut sum = 0;
for n in &numbers {
sum += n;
}
// 推荐
let sum: i32 = numbers.iter().sum();
// 链式操作
let result: Vec<i32> = numbers
.iter()
.filter(|x| *x % 2 == 0)
.map(|x| x * 2)
.collect();
}
9. 使用 ? 传播错误
rust
use std::fs::File;
use std::io::Read;
// 不推荐
fn read_file() -> Result<String, std::io::Error> {
let f = File::open("data.txt");
let mut f = match f {
Ok(file) => file,
Err(e) => return Err(e),
};
let mut s = String::new();
match f.read_to_string(&mut s) {
Ok(_) => Ok(s),
Err(e) => Err(e),
}
}
// 推荐
fn read_file_better() -> Result<String, std::io::Error> {
let mut s = String::new();
File::open("data.txt")?.read_to_string(&mut s)?;
Ok(s)
}
// 最推荐
fn read_file_best() -> Result<String, std::io::Error> {
std::fs::read_to_string("data.txt")
}
10. 使用 derive 减少样板代码
rust
// 使用 derive 自动实现常用 trait
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
struct Person {
name: String,
age: u32,
}
fn main() {
let p1 = Person {
name: String::from("张三"),
age: 25,
};
let p2 = p1.clone();
println!("{:?}", p1); // Debug
println!("{}", p1 == p2); // PartialEq
}
总结
Rust 的核心概念:
- 所有权 - 每个值有且只有一个所有者,离开作用域自动释放
- 借用 - 通过引用临时访问值,不获取所有权
- 生命周期 - 确保引用始终有效,防止悬垂指针
学习路线建议
rust
入门阶段(1-2周)
├── 基本语法(变量、函数、控制流)
├── 所有权系统(重点!)
├── 引用与借用
└── 结构体和枚举
进阶阶段(2-4周)
├── 模式匹配
├── 错误处理(Option、Result)
├── 泛型和 Trait
├── 生命周期标注
└── 闭包和迭代器
实战阶段(持续)
├── 模块系统
├── 常用集合(Vec、HashMap)
├── 智能指针
├── 并发编程
└── 异步编程(async/await)
推荐资源
- 官方书籍:《The Rust Programming Language》(Rust 程序设计语言)
- 练习网站 :Rustlings(github.com/rust-lang/r...
- 实战项目 :Rust by Example(doc.rust-lang.org/rust-by-exa...
- 社区:Rust 中文社区、RustCC
常见编译错误速查
| 错误信息 | 原因 | 解决方法 |
|---|---|---|
borrow of moved value |
值被移动后使用 | 使用引用 & 或 .clone() |
cannot borrow as mutable |
不可变变量借为可变 | 加 mut 或重新设计 |
cannot borrow as mutable more than once |
同时有多个可变引用 | 缩小引用作用域 |
lifetime may not live long enough |
生命周期不明确 | 添加生命周期标注 |
use of possibly uninitialized variable |
变量可能未初始化 | 确保所有路径都初始化 |
最后的话:Rust 确实有学习曲线,但一旦你理解了所有权系统,你会发现它其实很符合直觉。编译器虽然严格,但它是在帮你避免运行时错误。坚持写下去,你会发现 Rust 的魅力!
祝你学习愉快!🦀