Rust 从零学习指南
> 对应官方文档 Chapter 1: Getting Started
前端架构师通关手册 · 专题 18
本指南基于 [The Rust Programming Language](https://doc.rust-lang.org/stable/book/)(官方圣经)的结构编写,
每个知识点都配有详细讲解和可运行的 Demo 示例。
> 适用版本:Rust 1.97.0+,Edition 2024
第一部分:环境搭建与 Hello Cargo
对应官方文档 Chapter 1: Getting Started
1.1 安装 Rust
使用 rustup 安装(推荐)
rustup 是 Rust 的官方工具链管理器,用于安装、更新和管理 Rust 版本。
Windows 安装
- 下
载安装器:https://www.rust-lang.org/tools/install
- 运
行
rustup-init.exe,按提示安装
- 安
装过程中会自动安装 Microsoft C++ Build Tools(如未安装)
macOS / Linux 安装
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
验证安装
rustc --version # 查看 Rust 编译器版本
cargo --version # 查看 Cargo 包管理器版本
rustup --version # 查看 rustup 版本
Rust 工具链说明
| 工具 | 用途 | 说明 |
|---|
rustc | Rust 编译器 | 将 .rs 文件编译为可执行文件或库 |
cargo | 包管理器 + 构建系统 | 管理依赖、编译、测试、发布 |
rustup | 工具链管理器 | 安装/切换 Rust 版本、管理目标平台 |
rustfmt | 代码格式化 | 统一代码风格 |
clippy | 代码检查工具 | 发现常见错误和改进建议 |
rustdoc | 文档生成器 | 从注释生成 HTML 文档 |
更新与卸载
rustup update # 更新 Rust 到最新版本
rustup self uninstall # 卸载 Rust
1.2 Hello World
直接编译运行
// main.rs
fn main() {
println!("Hello, world!");
}
rustc main.rs # 编译生成可执行文件
./main # 运行(Windows 上是 main.exe)
关于 fn main()
fn 声明一个函数main 是程序入口点,每个可执行程序必须有且仅有一个 main 函数println! 是一个宏(macro),不是函数。! 表示调用宏而非普通函数- 宏在编译时展开,可以做比普通函数更复杂的事情(如可变参数、语法扩展等)
1.3 Hello Cargo
什么是 Cargo?
Cargo 是 Rust 的构建系统和包管理器。它可以:
- 创建和管理项目
- 下载和编译依赖
- 运行测试
- 生成文档
- 发布到 crates.io
创建新项目
# 创建二进制项目(可执行文件)
cargo new hello_cargo
cd hello_cargo
# 创建库项目
cargo new --lib my_library
项目结构
hello_cargo/
├── Cargo.toml # 项目配置文件(类似 package.json / build.gradle)
├── .gitignore # Git 忽略文件(自动生成)
└── src/
└── main.rs # 入口文件
Cargo.toml 文件解析
[package]
name = "hello_cargo" # 项目名称
version = "0.1.0" # 版本号(语义化版本)
edition = "2024" # Rust 版本(2015/2018/2021/2024)
authors = ["Your Name <you@example.com>"]
description = "A sample project"
license = "MIT"
[dependencies] # 依赖项(第三方 crate)
Edition 说明:
edition = "2015":原始版本edition = "2018":引入模块系统改进、async/await 等edition = "2021":改进闭包捕获、panic 一致性等edition = "2024":最新版本,改进异步、模式匹配等
构建与运行
# 编译并运行(Debug 模式,编译快,运行慢)
cargo run
# 仅编译(不运行)
cargo build
# 发布编译(Release 模式,编译慢,运行快,有优化)
cargo build --release
# 快速检查编译错误(不生成可执行文件,比 build 更快)
cargo check
# 格式化代码
cargo fmt
# 运行 clippy 检查
cargo clippy
Cargo 常用命令速查
| 命令 | 说明 |
|---|
cargo new <name> | 创建新项目 |
cargo new --lib <name> | 创建库项目 |
cargo build | Debug 模式编译 |
cargo build --release | Release 模式编译 |
cargo run | 编译并运行 |
cargo check | 仅检查编译错误 |
cargo test | 运行测试 |
cargo doc --open | 生成并打开文档 |
cargo add <crate> | 添加依赖 |
cargo update | 更新依赖 |
cargo clean | 清理构建产物 |
cargo fmt | 格式化代码 |
cargo clippy | 代码检查 |
Demo 1.1:创建并运行第一个项目
# 步骤 1:创建项目
cargo new demo_hello
cd demo_hello
# 步骤 2:查看生成的代码
cat src/main.rs
# 输出:
# fn main() {
# println!("Hello, world!");
# }
# 步骤 3:运行
cargo run
# 输出:Hello, world!
# 步骤 4:修改代码
echo 'fn main() { println!("你好,Rust!"); }' > src/main.rs
# 步骤 5:重新运行
cargo run
# 输出:你好,Rust!
第二部分:基础编程概念
对应官方文档 Chapter 3: Common Programming Concepts
2.1 变量与可变性
不可变变量(默认)
fn main() {
let x = 5;
println!("x 的值是: {}", x);
// x = 6; // ❌ 编译错误!x 是不可变的
// error[E0384]: cannot assign twice to immutable variable `x`
}
为什么默认不可变?
- 不可变变量更容易推理
- 减少意外修改导致的 bug
- 编译器可以进行更多优化
可变变量
fn main() {
let mut x = 5;
println!("x 的值是: {}", x); // 5
x = 6; // ✅ 正确!x 是可变的
println!("x 的值是: {}", x); // 6
}
变量遮蔽(Shadowing)
fn main() {
let x = 5;
// 遮蔽:用同名新变量覆盖旧变量
// 注意:这不是修改原变量,而是创建了一个新变量
let x = x + 1; // 这里是 i32,值为 6
{
// 内部作用域遮蔽
let x = x * 2; // 值为 12
println!("内部 x: {}", x); // 12
}
// 内部作用域结束,遮蔽消失
println!("外部 x: {}", x); // 6
// 遮蔽可以改变类型 —— 这是 mut 做不到的
let spaces = " "; // &str 类型
let spaces = spaces.len(); // usize 类型
// 如果写成 let mut spaces = "..."; spaces = spaces.len(); 会报错!
}
遮蔽 vs mut 的区别
| 特性 | mut | 遮蔽(Shadowing) |
|---|
| 改变值 | ✅ | ✅ |
| 改变类型 | ❌ | ✅ |
| 创建新变量 | ❌ | ✅ |
| 内存地址 | 不变 | 改变 |
| 作用域 | 整个作用域 | 从遮蔽点到作用域结束 |
常量(Constants)
// 常量:必须标注类型,必须能在编译时求值
const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3;
const MAX_POINTS: u32 = 100_000; // 数字可以用 _ 分隔提高可读性
fn main() {
println!("最大点数: {}", MAX_POINTS);
println!("三小时秒数: {}", THREE_HOURS_IN_SECONDS);
}
常量 vs 不可变变量的区别:
| 特性 | 不可变变量 let | 常量 const |
|---|
| 类型标注 | 可选(推断) | 必须 |
| 遮蔽 | 可以 | 不可以 |
| 作用域 | 块级作用域 | 全局/块级 |
| 值 | 运行时计算 | 编译时常量 |
| 内存 | 有内存地址 | 内联到使用处 |
Demo 2.1:变量遮蔽的实际应用
fn main() {
// 场景:用户输入字符串,需要转换为数字
let input = "42"; // 用户输入(字符串)
println!("输入: {}", input);
let input = input.trim(); // 去除空白(遮蔽,仍是 &str)
println!("去除空白: {}", input);
let input: u32 = input.parse() // 解析为数字(遮蔽,改变类型)
.expect("请输入有效数字");
println!("解析后: {}", input);
let input = input * 2; // 计算(遮蔽)
println!("翻倍: {}", input);
}
2.2 数据类型
Rust 是静态类型语言,所有变量在编译时必须已知类型。但 Rust 支持类型推断,大多数情况下不需要显式标注。
标量类型(Scalar Types)
标量类型代表单个值。Rust 有四种标量类型:整数、浮点数、布尔值和字符。
整数类型
| 长度 | 有符号 | 无符号 | 范围 |
|---|
| 8位 | i8 | u8 | i8: -128~127, u8: 0~255 |
| 16位 | i16 | u16 | i16: -32768~32767 |
| 32位 | i32 | u32 | i32: 约 ±21亿 |
| 64位 | i64 | u64 | i64: 约 ±922京 |
| 128位 | i128 | u128 | 极大范围 |
| 架构 | isize | usize | 32位系统32位,64位系统64位 |
fn main() {
let x = 42; // 默认 i32
let x: i64 = 42; // 显式类型标注
let x = 42i64; // 类型后缀
let x = 0xff; // 十六进制
let x = 0o77; // 八进制
let x = 0b1111_0000; // 二进制
let x = b'A'; // 字节 (u8),值为 65
// 整数字面量中的 _ 只是可读性分隔符,不影响值
let x = 1_000_000; // 等于 1000000
let x = 0xff_ff_ff_ff; // 等于 4294967295
}
整数溢出:
- Debug 模式下:溢出会 panic
- Release 模式下:溢出会回绕(如 255u8 + 1 = 0)
浮点类型
fn main() {
let x = 2.0; // f64(默认,精度更高)
let y: f32 = 3.0; // f32(单精度)
// 浮点数运算
let sum = 5.0 + 10.0;
let difference = 95.5 - 4.3;
let product = 4.0 * 30.0;
let quotient = 56.7 / 32.2;
let remainder = 55.0 % 3.0;
}
布尔类型
fn main() {
let t = true;
let f: bool = false;
// 布尔值在条件判断中使用
if t {
println!("t 是 true");
}
}
字符类型
fn main() {
let c = 'z';
let z = 'ℤ';
let heart_eyed_cat = '😻'; // Rust 的 char 是 4 字节 Unicode 标量值
// 注意:char 是单引号,字符串是双引号
let a = 'a'; // char,4 字节
let b = "a"; // &str,字符串切片
}
为什么 Rust 的 char 是 4 字节?
- Unicode 标量值最大需要 4 字节表示
- 与 UTF-8 编码不同(UTF-8 是 1-4 字节变长)
复合类型(Compound Types)
复合类型可以将多个值组合成一种类型。Rust 有两种原生复合类型:元组和数组。
元组(Tuple)
fn main() {
let tup: (i32, f64, u8) = (500, 6.4, 1);
// 解构(destructuring)
let (x, y, z) = tup;
println!("y 的值是: {}", y); // 6.4
// 索引访问
let five_hundred = tup.0; // 500
let six_point_four = tup.1; // 6.4
let one = tup.2; // 1
}
元组的特点:
- 固定长度,创建后不能改变大小
- 可以包含不同类型的元素
- 常用于函数返回多个值
数组(Array)
fn main() {
let a = [1, 2, 3, 4, 5]; // 固定长度
let a: [i32; 5] = [1, 2, 3, 4, 5]; // 显式类型和长度
let a = [3; 5]; // [3, 3, 3, 3, 3]
// 访问元素
let first = a[0];
let second = a[1];
// 越界访问会 panic(运行时错误)
// let invalid = a[10]; // thread 'main' panicked at 'index out of bounds'
}
数组 vs 向量(Vec):
- 数组:固定长度,栈上分配
- Vec:动态长度,堆上分配
何时使用数组:
- 元素数量在编译时已知
- 需要栈上分配(性能敏感)
- 例如:月份名称、一年的天数
Demo 2.2:温度转换器
fn main() {
let celsius: f64 = 25.0;
let fahrenheit = celsius_to_fahrenheit(celsius);
println!("{}°C = {}°F", celsius, fahrenheit);
let fahrenheit: f64 = 77.0;
let celsius = fahrenheit_to_celsius(fahrenheit);
println!("{}°F = {}°F", fahrenheit, celsius);
}
fn celsius_to_fahrenheit(c: f64) -> f64 {
c * 9.0 / 5.0 + 32.0
}
fn fahrenheit_to_celsius(f: f64) -> f64 {
(f - 32.0) * 5.0 / 9.0
}
2.3 函数
基本函数
fn main() {
println!("Hello, world!");
another_function(5, 10);
}
fn another_function(x: i32, y: i32) {
println!("x = {}, y = {}", x, y);
}
函数命名规范:snake_case(小写字母 + 下划线)
语句与表达式
这是 Rust 的核心概念!
fn main() {
// 语句(Statement):执行操作但不返回值,以 ; 结尾
let x = 5; // let 语句
// ❌ 报错:let 语句没有返回值
// let y = (let x = 5);
// 表达式(Expression):计算并返回值,没有 ;
let y = {
let x = 3;
x + 1 // 没有分号,这是返回值(表达式)
};
// y = 4
// 代码块 {} 是表达式,返回最后一个表达式的值
let z = {
2 + 2 // 返回 4
};
}
关键区别:
- 语句不返回值,以
; 结尾 - 表达式返回值,没有
; - 如果在表达式末尾加
;,它就变成了语句,返回 ()
带返回值的函数
// 使用 -> 标注返回类型
fn five() -> i32 {
5 // 没有分号的表达式就是返回值
}
fn plus_one(x: i32) -> i32 {
x + 1 // 返回 x + 1
}
// ❌ 错误:加了分号变成语句,返回 (),与声明的 i32 不匹配
// fn plus_one_wrong(x: i32) -> i32 {
// x + 1; // 这是一个语句,返回 ()
// }
fn main() {
let x = five();
let y = plus_one(x);
println!("x = {}, y = {}", x, y); // x = 5, y = 6
}
发散函数(Diverging Functions)
// 发散函数永不返回,返回类型是 !
fn never_returns() -> ! {
panic!("这个函数永不返回");
}
// 用途:panic!、loop + break、exit 等
fn forever() -> ! {
loop {
println!("永远循环...");
std::thread::sleep(std::time::Duration::from_secs(1));
}
}
Demo 2.3:计算斐波那契数
fn main() {
for n in 0..10 {
println!("fib({}) = {}", n, fibonacci(n));
}
}
fn fibonacci(n: u32) -> u64 {
if n == 0 {
return 0;
}
if n == 1 {
return 1;
}
let mut prev = 0u64;
let mut curr = 1u64;
for _ in 2..=n {
let next = prev + curr;
prev = curr;
curr = next;
}
curr
}
2.4 控制流
if 表达式
fn main() {
let number = 6;
// if 表达式
if number % 4 == 0 {
println!("能被 4 整除");
} else if number % 3 == 0 {
println!("能被 3 整除");
} else {
println!("不能被 4 或 3 整除");
}
// if 作为表达式(各分支返回类型必须一致)
let condition = true;
let number = if condition { 5 } else { 0 };
println!("number = {}", number); // 5
// ❌ 错误:分支返回类型不一致
// let number = if condition { 5 } else { "six" };
}
循环
loop 无限循环
fn main() {
let mut counter = 0;
// loop 可以返回值
let result = loop {
counter += 1;
if counter == 10 {
break counter * 2; // break 可以返回值
}
};
println!("result = {}", result); // 20
// 嵌套循环与标签
let mut count = 0;
'outer: loop {
println!("外层: {}", count);
let mut remaining = 10;
loop {
println!("内层: {}", remaining);
if remaining == 9 {
break; // 跳出内层循环
}
if count == 2 {
break 'outer; // 跳出外层循环
}
remaining -= 1;
}
count += 1;
}
}
while 循环
fn main() {
let mut number = 3;
while number != 0 {
println!("{}!", number);
number -= 1;
}
println!("发射!");
}
for 循环
fn main() {
// 遍历数组
let arr = [10, 20, 30, 40, 50];
for element in arr.iter() {
println!("值: {}", element);
}
// 使用 Range
for number in 1..4 { // 1, 2, 3(不包括 4)
println!("{}!", number);
}
// 反转 Range
for number in (1..4).rev() { // 3, 2, 1
println!("{}!", number);
}
// 遍历集合
let names = vec!["Alice", "Bob", "Charlie"];
for name in names.iter() {
println!("Hello, {}!", name);
}
}
Demo 2.4:打印乘法表
fn main() {
for i in 1..=9 {
for j in 1..=i {
print!("{}×{}\t", j, i);
}
println!();
}
}
2.5 注释
fn main() {
// 单行注释
/*
* 块注释
* 可以跨越多行
*/
/// 文档注释(对外 API)
/// 支持 Markdown 语法
/// 使用 `cargo doc` 生成文档
//! 模块级文档注释
//! 描述包含它的模块或 crate
let x = 5; // 行尾注释
}
第三部分:所有权系统(核心!)
对应官方文档 Chapter 4: Understanding Ownership
3.1 什么是所有权?
所有权规则
- R
ust 中的每个值都有一个
所有者(owner)
- 一
次只能有一个所有者
- 当
所有者离开作用域,值将被
丢弃(drop)
为什么需要所有权?
传统语言管理内存的方式:
- 手动管理(C/C++):程序员手动分配/释放,容易出错
- 垃圾回收(Java/Go/JS):运行时自动回收,有性能开销
- 所有权系统(Rust):编译时检查,零成本抽象
内存模型
栈 (Stack) 堆 (Heap)
┌─────────────┐ ┌────────────────────┐
│ name: ptr │ ──────────► │ h │
│ len: 5 │ │ e │
│ capacity: 5 │ │ l │
└─────────────┘ │ l │
String 元数据 │ o │
└────────────────────┘
实际字符串数据
栈 vs 堆:
- 栈:快速,固定大小,LIFO
- 堆:动态大小,需要分配/释放,有指针开销
字符串字面量 vs String
fn main() {
// 字符串字面量:固定大小,编译时已知,存储在栈上
let s1 = "hello";
// s1 是 &str(字符串切片),指向二进制文件中的数据
// 不可变,编译时大小已知
// String:可变,存储在堆上
let s2 = String::from("hello");
s2.push_str(", world!"); // 可变
// String 在堆上分配,运行时大小可变
}
3.2 移动(Move)
浅拷贝 vs 深拷贝 vs 移动
fn main() {
// 栈上的数据:复制(Copy)
let x = 5;
let y = x; // 复制而非移动(整数实现了 Copy trait)
println!("x = {}, y = {}", x, y); // 都有效
// 堆上的数据:移动(Move)
let s1 = String::from("hello");
let s2 = s1; // s1 被"移动"到 s2,s1 不再有效
// println!("{}", s1); // ❌ 编译错误!s1 已被移动
println!("{}", s2); // ✅ 正确
}
为什么移动而不是复制?
- 如果 s1 和 s2 都指向同一块堆内存
- 离开作用域时都会尝试释放(double free 错误)
- Rust 通过移动语义避免这个问题
克隆(Clone)
fn main() {
let s1 = String::from("hello");
let s2 = s1.clone(); // 深度复制(堆上的数据也被复制)
println!("s1 = {}, s2 = {}", s1, s2); // 都有效
}
Copy trait
fn main() {
// 实现了 Copy trait 的类型:
// - 所有整数类型(i32, u64 等)
// - 浮点类型(f32, f64)
// - 布尔类型(bool)
// - 字符类型(char)
// - 元组,如果所有元素都是 Copy 的
let a = (1, 2.0); // ✅ 元组内都是 Copy 类型
let b = a;
println!("a = {:?}, b = {:?}", a, b); // 都有效
let c = (1, String::from("hello")); // ❌ String 不是 Copy
let d = c;
// println!("{:?}", c); // ❌ c 已被移动
println!("{:?}", d); // ✅
}
Copy trait 的规则:
- 如果一个类型实现了 Copy,旧的变量在赋值后仍然可用
- 任何简单标量值的组合都可以是 Copy
- 需要分配内存或某种资源的类型不能是 Copy
函数中的所有权
fn main() {
let s = String::from("hello");
takes_ownership(s); // s 被移动到函数中
// println!("{}", s); // ❌ s 不再有效
let x = 5;
makes_copy(x); // x 被复制到函数中
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 离开作用域,没有特殊操作
返回值与作用域
fn main() {
let s1 = gives_ownership(); // 返回值移动到 s1
let s2 = String::from("hello");
let s3 = takes_and_gives_back(s2); // s2 移动到函数,返回值移动到 s3
println!("s1 = {}, s3 = {}", s1, s3);
}
fn gives_ownership() -> String {
let some_string = String::from("yours");
some_string // 返回值移动到调用者
}
fn takes_and_gives_back(a_string: String) -> String {
a_string // 返回值移动到调用者
}
Demo 3.1:所有权转移的完整示例
fn main() {
// 场景:用户注册流程
let username = String::from("alice");
let email = String::from("alice@example.com");
// 创建用户(所有权转移到 User 结构体)
let user = create_user(username, email);
// println!("{}", username); // ❌ username 已被移动
println!("用户创建成功: {:?}", user);
// 更新邮箱(需要重新赋值)
let user = update_email(user, String::from("newalice@example.com"));
println!("更新后: {:?}", user);
}
#[derive(Debug)]
struct User {
username: String,
email: String,
}
fn create_user(username: String, email: String) -> User {
User { username, email }
}
fn update_email(mut user: User, new_email: String) -> User {
user.email = new_email;
user
}
3.3 引用与借用
引用(Reference)
fn main() {
let s1 = String::from("hello");
// &s1 是引用,不拥有所有权
let len = calculate_length(&s1);
println!("'{}' 的长度是 {}", s1, len); // s1 仍然有效
}
// &String 表示对 String 的引用
fn calculate_length(s: &String) -> usize {
s.len()
} // s 离开作用域但不释放内存(不拥有所有权)
s1 s
┌───────────┐ ┌───────────┐
│ ptr │ ──────► │ ptr │ ──► "hello"
│ len: 5 │ └───────────┘
│ cap: 5 │ 引用(指针)
└───────────┘
可变引用(Mutable Reference)
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");
}
借用规则(核心!)
核心规则:
1. 同一时间,只能有一个可变引用
2. 或者可以有多个不可变引用
3. 引用必须始终有效
fn main() {
let mut s = String::from("hello");
// 多个不可变引用 - ✅ 允许
let r1 = &s;
let r2 = &s;
println!("{} and {}", r1, r2);
// ❌ 错误:不可变引用存在时不能有可变引用
// let r3 = &mut s;
// error[E0502]: cannot borrow `s` as mutable because it is also borrowed as immutable
// ✅ 正确:在不可变引用不再使用后(NLL: Non-Lexical Lifetimes)
let r3 = &mut s;
println!("{}", r3);
}
悬垂引用(Dangling Reference)
// ❌ 编译错误!不能返回局部变量的引用
// fn dangle() -> &String {
// let s = String::from("hello");
// &s // s 离开作用域被释放,引用指向无效内存
// }
// error[E0106]: missing lifetime specifier
// ✅ 正确:直接返回 String
fn no_dangle() -> String {
let s = String::from("hello");
s
}
Demo 3.2:引用的实际应用
fn main() {
let mut numbers = vec![1, 2, 3, 4, 5];
// 不可变借用:读取数据
let first = &numbers[0];
println!("第一个元素: {}", first);
// 可变借用:修改数据
for num in &mut numbers {
*num *= 2;
}
println!("翻倍后: {:?}", numbers);
// 使用引用避免所有权转移
let sum = calculate_sum(&numbers);
println!("总和: {}", sum);
println!("numbers 仍然可用: {:?}", numbers);
}
fn calculate_sum(numbers: &[i32]) -> i32 {
numbers.iter().sum()
}
3.4 切片(Slice)
字符串切片
fn main() {
let s = String::from("hello world");
let hello = &s[0..5]; // "hello"
let world = &s[6..11]; // "world"
let hello = &s[..5]; // 省略起始(从 0 开始)
let world = &s[6..]; // 省略结束(到末尾)
let whole = &s[..]; // 全部
println!("{} {}", hello, world);
}
第一个单词函数
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 s = String::from("hello world");
let word = first_word(&s);
println!("第一个单词: {}", word);
}
其他类型的切片
fn main() {
let a = [1, 2, 3, 4, 5];
let slice = &a[1..3]; // [2, 3]
assert_eq!(slice, &[2, 3]);
// 切片是对连续序列的引用
// 字符串切片 &str 和数组切片 &[T] 都是切片
}
Demo 3.3:字符串处理工具
fn main() {
let text = "The quick brown fox jumps over the lazy dog";
println!("原文: {}", text);
println!("第一个单词: {}", first_word(text));
println!("最后一个单词: {}", last_word(text));
println!("单词数量: {}", count_words(text));
}
fn first_word(s: &str) -> &str {
s.split_whitespace().next().unwrap_or("")
}
fn last_word(s: &str) -> &str {
s.split_whitespace().last().unwrap_or("")
}
fn count_words(s: &str) -> usize {
s.split_whitespace().count()
}
第四部分:结构体
对应官方文档 Chapter 5: Using Structs to Structure Related Data
4.1 定义与创建结构体
// 定义结构体
struct User {
username: String,
email: String,
sign_in_count: u64,
active: bool,
}
fn main() {
// 创建实例(字段顺序可以不同,但名称必须匹配)
let user1 = User {
email: String::from("someone@example.com"),
username: String::from("someusername"),
active: true,
sign_in_count: 1,
};
// 访问字段
println!("用户名: {}", user1.username);
// 可变实例(整个实例必须是可变的,不能只标记某些字段为 mut)
let mut user2 = User {
email: String::from("another@example.com"),
username: String::from("anotherusername"),
active: true,
sign_in_count: 1,
};
user2.email = String::from("new@example.com");
}
字段初始化简写
fn build_user(email: String, username: String) -> User {
User {
email, // email: email 的简写
username, // username: username 的简写
active: true,
sign_in_count: 1,
}
}
从其他实例创建(结构体更新语法)
fn main() {
let user1 = User {
email: String::from("someone@example.com"),
username: String::from("someusername"),
active: true,
sign_in_count: 1,
};
// 结构体更新语法(..user1)
let user2 = User {
email: String::from("another@example.com"),
username: String::from("anotherusername"),
..user1 // 其余字段来自 user1
};
// ⚠️ 注意:如果 user1 的字段中有非 Copy 类型(如 String),
// user1 的 username 字段会被移动到 user2
// println!("{}", user1.username); // ❌ 已被移动
println!("{}", user1.active); // ✅ bool 是 Copy 类型
}
元组结构体
// 元组结构体:有名称但没有命名字段
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 = ({}, {}, {})", black.0, black.1, black.2);
println!("origin = ({}, {}, {})", origin.0, origin.1, origin.2);
// 虽然 Color 和 Point 内部结构相同,但它们是不同的类型
// let p: Point = black; // ❌ 类型不匹配
}
单元结构体(Unit-Like Structs)
// 单元结构体:没有任何字段
// 常用于实现 trait 但不需要存储数据的情况
struct AlwaysEqual;
fn main() {
let subject = AlwaysEqual;
// 可以为其实现 trait
// impl SomeTrait for AlwaysEqual { ... }
}
Demo 4.1:矩形面积计算
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
fn main() {
let rect1 = Rectangle { width: 30, height: 50 };
let rect2 = Rectangle { width: 10, height: 40 };
let rect3 = Rectangle { width: 60, height: 45 };
println!("rect1 的面积: {}", area(&rect1));
println!("rect1: {:?}", rect1); // 使用 Debug trait 打印
println!("rect1 能容纳 rect2 吗?{}", rect1.can_hold(&rect2));
println!("rect1 能容纳 rect3 吗?{}", rect1.can_hold(&rect3));
// 使用关联函数创建正方形
let sq = Rectangle::square(5);
println!("正方形: {:?}", sq);
}
fn area(rectangle: &Rectangle) -> u32 {
rectangle.width * rectangle.height
}
// 为结构体实现方法
impl Rectangle {
// &self 是 self: &Self 的简写
fn can_hold(&self, other: &Rectangle) -> bool {
self.width > other.width && self.height > other.height
}
// 关联函数(没有 self 参数)
fn square(size: u32) -> Self {
Self {
width: size,
height: size,
}
}
}
4.2 方法(Method)
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
// 实现块:定义方法
impl Rectangle {
// 不可变方法
fn area(&self) -> u32 {
self.width * self.height
}
// 可变方法
fn resize(&mut self, width: u32, height: u32) {
self.width = width;
self.height = height;
}
// 消耗 self 的方法
fn destroy(self) {
println!("销毁矩形: {}x{}", self.width, self.height);
}
// 关联函数(没有 self)
fn square(size: u32) -> Self {
Self {
width: size,
height: size,
}
}
}
// 一个类型可以有多个 impl 块(功能上等价于一个)
impl Rectangle {
fn perimeter(&self) -> u32 {
2 * (self.width + self.height)
}
}
fn main() {
let mut rect = Rectangle { width: 30, height: 50 };
println!("面积: {}", rect.area());
println!("周长: {}", rect.perimeter());
rect.resize(40, 60);
println!("调整后面积: {}", rect.area());
let sq = Rectangle::square(10);
println!("正方形: {:?}", sq);
rect.destroy();
// rect.area(); // ❌ rect 已被销毁
}
self 参数的类型
| 方法签名 | 等价于 | 效果 |
|---|
fn f(&self) | fn f(self: &Self) | 不可变借用 |
fn f(&mut self) | fn f(self: &mut Self) | 可变借用 |
fn f(self) | fn f(self: Self) | 获取所有权 |
自动引用和解引用
// Rust 会自动添加 &、&mut 或 * 来匹配方法签名
let rect = Rectangle { width: 1, height: 2 };
rect.area(); // 等价于 (&rect).area()
(&rect).area(); // 显式借用
(&mut rect).resize(3, 4); // 显式可变借用
第五部分:枚举与模式匹配
对应官方文档 Chapter 6: Enums and Pattern Matching
5.1 定义枚举
// 基本枚举
enum IpAddrKind {
V4,
V6,
}
// 带数据的枚举
enum IpAddr {
V4(u8, u8, u8, u8),
V6(String),
}
// 枚举可以包含不同类型和数量的数据
enum Message {
Quit, // 无数据
Move { x: i32, y: i32 }, // 匿名结构体
Write(String), // 单个 String
ChangeColor(i32, i32, i32), // 三个 i32
}
// 枚举也可以有 impl 块
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!("颜色: ({}, {}, {})", r, g, b),
}
}
}
fn main() {
let home = IpAddr::V4(127, 0, 0, 1);
let loopback = IpAddr::V6(String::from("::1"));
let m = Message::Write(String::from("hello"));
m.call();
}
Option 枚举
// Rust 没有 null,而是用 Option 枚举
// 定义在标准库中:
// enum Option<T> {
// None, // 空值
// Some(T), // 有值
// }
fn main() {
let some_number = Some(5);
let some_string = Some("a string");
let absent_number: Option<i32> = None;
// 使用 Option 避免 null 引用错误
// 必须处理 None 情况才能使用值
}
为什么 Option 比 null 安全?
- Rust 强制你处理
None 情况 - 不能把
Option<T> 当作 T 使用 - 编译器确保你不会忘记检查空值
5.2 match 模式匹配
#[derive(Debug)]
enum UsState {
Alabama,
Alaska,
// ...
}
enum Coin {
Penny,
Nickel,
Dime,
Quarter(UsState),
}
fn value_in_cents(coin: Coin) -> u8 {
match coin {
Coin::Penny => {
println!("幸运硬币!");
1
}
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter(state) => {
println!("来自 {:?} 的 quarter!", state);
25
}
}
}
fn main() {
let coin = Coin::Quarter(UsState::Alaska);
println!("价值: {} 分", value_in_cents(coin));
}
match 必须穷尽所有可能
fn plus_one(x: Option<i32>) -> Option<i32> {
match x {
None => None,
Some(i) => Some(i + 1),
}
// 如果遗漏 None 分支,编译器会报错
}
fn main() {
let five = Some(5);
let six = plus_one(five);
let none = plus_one(None);
println!("six = {:?}, none = {:?}", six, none);
}
占位符 _
fn main() {
let dice_roll = 9;
match dice_roll {
3 => add_fancy_hat(),
7 => remove_fancy_hat(),
other => move_player(other), // 捕获所有其他值
}
match dice_roll {
3 => add_fancy_hat(),
7 => remove_fancy_hat(),
_ => reroll(), // 不关心其他值,但不绑定
}
match dice_roll {
3 => add_fancy_hat(),
_ => (), // 什么都不做(unit 类型)
}
}
fn add_fancy_hat() {}
fn remove_fancy_hat() {}
fn move_player(num_spaces: u8) {}
fn reroll() {}
使用 match 处理 Option
fn main() {
let five = Some(5);
let six = plus_one(five);
let none = plus_one(None);
}
fn plus_one(x: Option<i32>) -> Option<i32> {
match x {
None => None,
Some(i) => Some(i + 1),
}
}
5.3 if let 简化匹配
fn main() {
let config_max = Some(3u8);
// 完整 match
match config_max {
Some(max) => println!("最大值被设置为 {}", max),
_ => (),
}
// 简化写法:if let
if let Some(max) = config_max {
println!("最大值被设置为 {}", max);
}
// 带 else
let coin = Coin::Penny;
let mut count = 0;
if let Coin::Quarter(state) = coin {
println!("来自 {:?} 的 quarter!", state);
} else {
count += 1;
}
}
Demo 5.1:解析配置值
fn main() {
let config = Config {
debug: true,
log_level: Some("info".to_string()),
port: Some(8080),
host: None,
};
println!("调试模式: {}", config.debug);
// 使用 if let 处理 Option
if let Some(level) = &config.log_level {
println!("日志级别: {}", level);
} else {
println!("日志级别: 未设置");
}
if let Some(port) = config.port {
println!("端口: {}",port);
}
if let Some(host) = &config.host {
println!("主机: {}", host);
} else {
println!("主机: 默认 (localhost)");
}
}
struct Config {
debug: bool,
log_level: Option<String>,
port: Option<u16>,
host: Option<String>,
}
第六部分:包、Crate 和模块
对应官方文档 Chapter 7: Managing Growing Projects with Packages, Crates, and Modules
6.1 包与 Crate
层次结构
包 (Package)
├── Cargo.toml # 包配置文件
└── src/
├── main.rs # 二进制 crate 根(入口)
├── lib.rs # 库 crate 根
└── bin/ # 额外的二进制 crate
└── extra.rs
规则:
- 一个包可以有多个二进制 crate(放在
src/bin/) - 一个包可以有零个或一个库 crate
- 包必须至少有一个 crate(二进制或库)
Crate 的类型
- 二进制 crate:编译为可执行文件,有
main 函数 - 库 crate:没有
main 函数,供其他项目使用
6.2 定义模块
// src/lib.rs
mod front_of_house {
mod hosting {
fn add_to_waitlist() {}
fn seat_at_table() {}
}
mod serving {
fn take_order() {}
fn serve_order() {}
fn take_payment() {}
}
}
模块树
crate
└── front_of_house
├── hosting
│ ├── add_to_waitlist
│ └── seat_at_table
└── serving
├── take_order
├── serve_order
└── take_payment
6.3 路径(Paths)
绝对路径和相对路径
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
}
pub fn eat_at_restaurant() {
// 绝对路径(从 crate 根开始)
crate::front_of_house::hosting::add_to_waitlist();
// 相对路径(从当前模块开始)
front_of_house::hosting::add_to_waitlist();
}
super 关键字
fn deliver_order() {}
mod back_of_house {
fn fix_incorrect_order() {
cook_order();
super::deliver_order(); // 访问父模块的函数
}
fn cook_order() {}
}
pub 关键字
mod back_of_house {
pub struct Breakfast {
pub toast: String,
seasonal_fruit: String, // 私有字段
}
impl Breakfast {
pub fn summer(toast: &str) -> Breakfast {
Breakfast {
toast: String::from(toast),
seasonal_fruit: String::from("peaches"),
}
}
}
// 枚举的变体在枚举为 pub 时自动为 pub
pub enum Appetizer {
Soup,
Salad,
}
}
pub fn eat_at_restaurant() {
let mut meal = back_of_house::Breakfast::summer("Rye");
meal.toast = String::from("Wheat");
println!("请给我 {:?} 面包", meal.toast);
// ❌ 错误:seasonal_fruit 是私有的
// meal.seasonal_fruit = String::from("blueberries");
}
默认私有规则:
- 模块默认私有
- 函数默认私有
- 结构体默认私有,字段也默认私有
- 枚举的变体在枚举为 pub 时自动为 pub
6.4 use 关键字
引入
--- 鎺ョ画 ---
Mutex
use std::sync::Mutex;
fn main() {
let m = Mutex::new(5);
{
let mut num = m.lock().unwrap();
*num = 6;
}
println!("m = {:?}", m);
}
Arc>
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
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());
}
Send 鍜?Sync
// Send: 鍙互鍦ㄧ嚎绋嬮棿杞Щ鎵€鏈夋潈
// Sync: 鍙互鍦ㄧ嚎绋嬮棿鍏变韩寮曠敤锛?T 鏄?Send锛?
// Rust 鑷姩涓哄ぇ澶氭暟绫诲瀷瀹炵幇 Send 鍜?Sync
// 鍘熷鎸囬拡 *const T 鍜?*mut T 娌℃湁瀹炵幇 Send 鍜?Sync
Demo 13.1锛氬绾跨▼涓嬭浇鍣紙妯℃嫙锛?
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;
fn main() {
let urls = vec![
"https://example.com/file1.txt",
"https://example.com/file2.txt",
"https://example.com/file3.txt",
"https://example.com/file4.txt",
];
let results = Arc::new(Mutex::new(Vec::new()));
let mut handles = vec![];
for url in urls {
let results = Arc::clone(&results);
let handle = thread::spawn(move || {
// 妯℃嫙涓嬭浇
println!("寮€濮嬩笅杞? {}", url);
thread::sleep(Duration::from_millis(100));
let content = format!("{} 鐨勫唴瀹?, url);
results.lock().unwrap().push(content);
println!("瀹屾垚涓嬭浇: {}", url);
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
let results = results.lock().unwrap();
println!("涓嬭浇瀹屾垚锛屽叡 {} 涓枃浠?", results.len());
for content in results.iter() {
println!(" - {}", content);
}
}
绗崄鍥涢儴鍒嗭細楂樼骇鐗规€?
14.1 涓嶅畨鍏?Rust
瀵瑰簲瀹樻柟鏂囨。 Chapter 20.1: Unsafe Rust
fn main() {
let mut num = 5;
// 鍘熷鎸囬拡
let r1 = &num as *const i32;
let r2 = &mut num as *mut i32;
// 瑙e紩鐢ㄥ師濮嬫寚閽堥渶瑕?unsafe
unsafe {
println!("r1 鏄? {}", *r1);
println!("r2 鏄? {}", *r2);
}
// 璋冪敤涓嶅畨鍏ㄥ嚱鏁?
unsafe {
dangerous();
}
// 瀹夊叏鎶借薄
let mut v = vec![1, 2, 3, 4, 5, 6];
let r = &mut v[..];
let (a, b) = split_at_mut(r, 3);
assert_eq!(a, &mut [1, 2, 3]);
assert_eq!(b, &mut [4, 5, 6]);
}
unsafe fn dangerous() {}
fn split_at_mut(values: &mut [i32], mid: usize) -> (&mut [i32], &mut [i32]) {
let len = values.len();
let ptr = values.as_mut_ptr();
assert!(mid <= len);
unsafe {
(
std::slice::from_raw_parts_mut(ptr, mid),
std::slice::from_raw_parts_mut(ptr.add(mid), len - mid),
)
}
}
*涓嶅畨鍏?Rust 鐨勮兘鍔?锛?
- 瑙
e紩鐢ㄥ師濮嬫寚閽?
- 璋
冪敤涓嶅畨鍏ㄥ嚱鏁版垨鏂规硶
- 璁
块棶鎴栦慨鏀瑰彲鍙橀潤鎬佸彉閲?
- 瀹
炵幇涓嶅畨鍏?trait
- 璁
块棶 union 鐨勫瓧娈?
*浣跨敤 unsafe 鐨勫父瑙佸満鏅?锛?
- 璋冪敤 C 浠g爜锛團FI锛?
- 瀹炵幇搴曞眰鎶借薄
- 鎬ц兘浼樺寲
extern 鍑芥暟
// 璋冪敤澶栭儴 C 浠g爜
extern "C" {
fn abs(input: i32) -> i32;
}
fn main() {
unsafe {
println!("C 鐨勭粷瀵瑰€? {}", abs(-3));
}
}
// 瀵煎嚭 Rust 鍑芥暟缁?C 浣跨敤
#[no_mangle]
pub extern "C" fn call_from_c() {
println!("鏉ヨ嚜 C 鐨勮皟鐢?);
}
14.2 楂樼骇 Trait
鍏宠仈绫诲瀷
pub trait Iterator {
type Item; // 鍏宠仈绫诲瀷
fn next(&mut self) -> Option<Self::Item>;
}
struct Counter {
count: u32,
}
impl Counter {
fn new() -> Counter {
Counter { count: 0 }
}
}
impl Iterator for Counter {
type Item = u32;
fn next(&mut self) -> Option<Self::Item> {
self.count += 1;
if self.count < 6 {
Some(self.count)
} else {
None
}
}
}
榛樿娉涘瀷鍙傛暟
use std::ops::Add;
#[derive(Debug, PartialEq)]
struct Millimeters(u32);
struct Meters(u32);
impl Add<Meters> for Millimeters {
type Output = Millimeters;
fn add(self, other: Meters) -> Millimeters {
Millimeters(self.0 + (other.0 * 1000))
}
}
瀹屽叏闄愬畾璇硶
trait Pilot {
fn fly(&self);
}
trait Wizard {
fn fly(&self);
}
struct Human;
impl Pilot for Human {
fn fly(&self) {
println!("鏈洪暱璇翠綘濂?);
}
}
impl Wizard for Human {
fn fly(&self) {
println!("璧烽锛?);
}
}
impl Human {
fn fly(&self) {
println!("鎸ユ墜");
}
}
fn main() {
let person = Human;
person.fly(); // 榛樿璋冪敤 Human 鐨勬柟娉?
Pilot::fly(&person); // 璋冪敤 Pilot trait
Wizard::fly(&person); // 璋冪敤 Wizard trait
}
// 瀹屽叏闄愬畾璇硶
// <Type as Trait>::function(receiver_if_method, next_arg, ...);
Supertrait
use std::fmt;
trait PrintOutline: fmt::Display {
fn print_outline(&self) {
let output = self.to_string();
let len = output.len();
println!("{}", "*".repeat(len + 4));
println!("* {} *", output);
println!("{}", "*".repeat(len + 4));
}
}
newtype 妯″紡
use std::fmt;
struct Wrapper(Vec<String>);
impl fmt::Display for Wrapper {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "[{}]", self.0.join(", "))
}
}
fn main() {
let w = Wrapper(vec![String::from("hello"), String::from("world")]);
println!("w = {}", w);
}
14.3 楂樼骇绫诲瀷
绫诲瀷鍒悕
type Kilometers = i32;
fn main() {
let x: i32 = 5;
let y: Kilometers = 5;
println!("x + y = {}", x + y);
}
// 鐢ㄩ€旓細鍑忓皯閲嶅
type Thunk = Box<dyn Fn() + Send + 'static>;
fn takes_long_type(f: Thunk) {
// ...
}
fn returns_long_type() -> Thunk {
Box::new(|| println!("hi"))
}
Never 绫诲瀷
// ! 绫诲瀷锛坣ever type锛夎〃绀烘案涓嶈繑鍥?
fn bar() -> ! {
panic!()
}
// 鐢ㄩ€旓細continue銆乸anic!銆乴oop + break
14.4 楂樼骇鍑芥暟涓庨棴鍖?
鍑芥暟鎸囬拡
fn add_one(x: i32) -> i32 {
x + 1
}
fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 {
f(arg) + f(arg)
}
fn main() {
let answer = do_twice(add_one, 5);
println!("绛旀鏄? {}", answer); // 12
// fn 鏄竴涓被鍨嬶紝瀹炵幇浜嗘墍鏈?Fn trait
let list_of_numbers = vec![1, 2, 3];
let list_of_strings: Vec<String> = list_of_numbers
.iter()
.map(|i| i.to_string())
.collect();
// 涔熷彲浠ヤ娇鐢ㄥ嚱鏁版寚閽?
let list_of_strings: Vec<String> = list_of_numbers
.iter()
.map(ToString::to_string)
.collect();
}
杩斿洖闂寘
// 鉂?閿欒锛氶棴鍖呭ぇ灏忔湭鐭?
// fn returns_closure() -> dyn Fn(i32) -> i32 {
// |x| x + 1
// }
// 鉁?姝g‘锛氫娇鐢?Box 鍖呰
fn returns_closure() -> Box<dyn Fn(i32) -> i32> {
Box::new(|x| x + 1)
}
14.5 瀹?
瀵瑰簲瀹樻柟鏂囨。 Chapter 20.5: Macros
澹版槑瀹忥紙macro_rules!锛?
// 绠€鍖栫殑 vec! 瀹?
#[macro_export]
macro_rules! vec {
( $( $x:expr ),* ) => {
{
let mut temp_vec = Vec::new();
$(
temp_vec.push($x);
)*
temp_vec
}
};
}
fn main() {
let v = vec![1, 2, 3];
println!("{:?}", v);
}
杩囩▼瀹?
// 杩囩▼瀹忕殑瀹氫箟鍦ㄥ崟鐙殑 crate 涓?
// 鏈変笁绉嶇被鍨嬶細
// 1. 绫诲睘鎬ц繃绋嬪畯锛坉erive锛?
// 2. 绫诲嚱鏁拌繃绋嬪畯
// 3. 绫诲睘鎬у畯
// 浣跨敤 derive 鐨勪緥瀛?
#[derive(Debug, Clone, PartialEq)]
struct Point {
x: i32,
y: i32,
}
// derive 鑷姩鐢熸垚 trait 瀹炵幇
缂栧啓鑷畾涔?derive 瀹?
// hello_macro.rs
pub trait HelloMacro {
fn hello_macro();
}
// hello_macro_derive.rs锛堝崟鐙殑 proc-macro crate锛?
use proc_macro::TokenStream;
use quote::quote;
use syn;
#[proc_macro_derive(HelloMacro)]
pub fn hello_macro_derive(input: TokenStream) -> TokenStream {
let ast = syn::parse(input).unwrap();
impl_hello_macro(&ast)
}
fn impl_hello_macro(ast: &syn::DeriveImpl) -> TokenStream {
let name = &ast.ident;
let gen = quote! {
impl HelloMacro for #name {
fn hello_macro() {
println!("Hello, Macro! My name is {}!", stringify!(#name));
}
}
};
gen.into()
}
绗崄浜旈儴鍒嗭細甯哥敤妯″紡
瀵瑰簲瀹樻柟鏂囨。 Chapter 19: Patterns and Matching
15.1 妯″紡鍖归厤璇硶
fn main() {
// 瀛楅潰閲?
let x = 1;
match x {
1 => println!("one"),
2 => println!("two"),
_ => println!("other"),
}
// 鍛藉悕鍙橀噺
let x = Some(5);
let y = 10;
match x {
Some(50) => println!("Got 50"),
Some(y) => println!("Matched, y = {:?}", y),
_ => println!("Default case, x = {:?}", x),
}
// 澶氭ā寮?
let x = 1;
match x {
1 | 2 => println!("one or two"),
_ => println!("other"),
}
// 鑼冨洿
let x = 5;
match x {
1..=5 => println!("one through five"),
_ => println!("other"),
}
// 瑙f瀯缁撴瀯浣?
struct Point { x: i32, y: i32 }
let p = Point { x: 0, y: 7 };
let Point { x: a, y: b } = p;
// 绠€鍐?
let Point { x, y } = p;
// 瑙f瀯鏋氫妇
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}
let msg = Message::ChangeColor(0, 160, 255);
match msg {
Message::Quit => println!("Quit"),
Message::Move { x, y } => println!("Move to ({}, {})", x, y),
Message::Write(text) => println!("Write: {}", text),
Message::ChangeColor(r, g, b) => println!("Color ({}, {}, {})", r, g, b),
}
// 宓屽瑙f瀯
let num = 4u8;
match num {
1 => println!("One"),
2 | 3 | 5 | 7 | 11 => println!("This is a prime"),
13..=19 => println!("A teen"),
_ => println!("Ain't special"),
}
// 蹇界暐鍊?
let mut setting_value = Some(5);
let new_setting_value = Some(10);
match (setting_value, new_setting_value) {
(Some(_), Some(_)) => println!("Can't overwrite"),
_ => setting_value = new_setting_value,
}
// .. 蹇界暐鍓╀綑閮ㄥ垎
struct Point3D { x: i32, y: i32, z: i32 }
let origin = Point3D { x: 0, y: 0, z: 0 };
match origin {
Point3D { x, .. } => println!("x is {}", x),
}
// 鍖归厤瀹堝崼
let num = Some(4);
match num {
Some(x) if x % 2 == 0 => println!("Even"),
Some(x) => println!("Odd"),
None => (),
}
// @ 缁戝畾
enum Greeting { Hello { id: i32 } }
let msg = Greeting::Hello { id: 5 };
match msg {
Greeting::Hello { id: id_variable @ 3..=7 } => println!("Found an id in range: {}", id_variable),
Greeting::Hello { id: 10..=12 } => println!("Found an id in another range"),
Greeting::Hello { id } => println!("Found some other id: {}", id),
}
}
let-else锛圧ust 1.65+锛?
fn main() {
let Some(x) = Some(5) else {
return;
};
println!("x = {}", x);
}
15.2 let 璇彞涓殑妯″紡
fn main() {
// 鍏冪粍瑙f瀯
let (x, y, z) = (1, 2, 3);
// 鍑芥暟鍙傛暟涓殑妯″紡
let point = (3, 5);
print_coordinates(&point);
}
fn print_coordinates(&(x, y): &(i32, i32)) {
println!("褰撳墠鍧愭爣: ({}, {})", x, y);
}
闄勫綍
A. Rust 缂栫爜瑙勮寖
鍛藉悕瑙勮寖
| 绫诲瀷 | 瑙勮寖 | 绀轰緥 |
|---|
| 鍙橀噺 | snake_case | user_name |
| 鍑芥暟 | snake_case | get_user |
| 缁撴瀯浣? | PascalCase | UserName |
| 鏋氫妇 | PascalCase | Color |
| trait | PascalCase | Printable |
| 甯搁噺 | SCREAMING_SNAKE_CASE | MAX_SIZE |
| 妯″潡 | snake_case | user_module |
| 瀹? | snake_case! | println! |
鏍煎紡鍖?
# 鏍煎紡鍖栦唬鐮?
cargo fmt
# 妫€鏌ヤ絾涓嶄慨鏀?
cargo fmt --check
# 杩愯 clippy 妫€鏌?
cargo clippy
B. 甯哥敤 Cargo 鍛戒护
# 鏂板缓椤圭洰
cargo new project_name
cargo new --lib lib_name
# 鏋勫缓
cargo build # debug 妯″紡
cargo build --release # release 妯″紡锛堜紭鍖栵級
# 杩愯
cargo run
# 妫€鏌ョ紪璇戦敊璇紙涓嶇敓鎴愬彲鎵ц鏂囦欢锛屾洿蹇級
cargo check
# 杩愯娴嬭瘯
cargo test
# 鐢熸垚鏂囨。
cargo doc --open
# 娣诲姞渚濊禆
cargo add package_name
# 鏇存柊渚濊禆
cargo update
# 娓呯悊鏋勫缓
cargo clean
# 鏋勫缓鐗瑰畾鐩爣
cargo build --target x86_64-pc-windows-msvc
C. 閿欒淇℃伅瑙h
| 閿欒绫诲瀷 | 鍚箟 | 瑙e喅鏂规 |
|---|
cannot borrow as mutable | 璇曞浘鍙彉鍊熺敤涓嶅彲鍙樺彉閲? | 娣诲姞 mut |
value used after move | 浣跨敤宸茬Щ鍔ㄧ殑鍊? | 浣跨敤 clone() 鎴栧紩鐢? |
lifetime mismatch | 鐢熷懡鍛ㄦ湡涓嶅尮閰? | 娣诲姞鐢熷懡鍛ㄦ湡鏍囨敞 |
trait not implemented | 缂哄皯 trait 瀹炵幇 | 瀹炵幇鐩稿簲 trait 鎴栦娇鐢?derive |
unused variable | 鏈娇鐢ㄧ殑鍙橀噺 | 浣跨敤 _ 鍓嶇紑鎴栧垹闄? |
expected type, found | 绫诲瀷涓嶅尮閰? | 妫€鏌ョ被鍨嬫爣娉? |
cannot infer type | 鏃犳硶鎺ㄦ柇绫诲瀷 | 娣诲姞绫诲瀷鏍囨敞 |
D. 瀛︿範璧勬簮姹囨€?
鍏嶈垂鍦ㄧ嚎璧勬簮
- <
em>*Rust 鍦g粡锛圱he Rust Programming Language锛?
- 鑻辨枃鐗堬細https://doc.rust-lang.org/stable/book/
- 涓枃鐗堬細https://kaisery.github.io/trpl-zh-cn/
- 鏈€浣冲叆闂ㄦ暀绋嬶紝绯荤粺鍏ㄩ潰
- <
strong>Rust by Example
- 涓枃鐗堬細https://rustwiki.org/zh-CN/rust-by-example/
- 閫氳繃绀轰緥瀛︿範
- <
strong>Rustlings
- https://github.com/rust-lang/rustlings
- 浜や簰寮忕粌涔狅紝淇缂栬瘧閿欒
- <
em>*Rust 鍦ㄧ嚎缂栬緫鍣?
- https://play.rust-lang.org/
- 鍦ㄧ嚎缂栧啓鍜岃繍琛?Rust 浠g爜
- <
em>*Rust 鏍囧噯搴撴枃妗?
- https://doc.rust-lang.org/std/
- 瀹屾暣鐨?API 鏂囨。
鎺ㄨ崘涔︾睄
- 銆奟ust 绋嬪簭璁捐璇█銆嬶紙瀹樻柟涓枃鐗堬級
- 銆奟ust 鏉冨▉鎸囧崡銆?
- 銆婃繁鍏ユ祬鍑?Rust銆?
- 銆奟ust 缂栫▼锛氬叆闂ㄣ€佸疄鎴樹笌杩涢樁銆?
绀惧尯
- Rust 涓枃绀惧尯锛歨ttps://rustcc.cn/
- Rust 瀹樻柟璁哄潧锛歨ttps://users.rust-lang.org/
- GitHub锛歨ttps://github.com/rust-lang
*绁濆涔犳剦蹇紒Rust 鏄竴闂ㄥ€煎緱鎶曞叆瀛︿範鐨勮瑷€銆? 馃