Cargo 基础与编程核心概念串讲

Cargo 基础与编程核心概念串讲
XIU今日教学目标
- 掌握 Cargo 项目管理工具的核心命令和工作流。
- 理解编程开发中的基本概念:值与类型、函数与方法。
- 了解指针与引用的基本概念(为 Day 08-09 所有权学习做铺垫)。
- 编写包含多文件结构的 Rust 程序,实践
mod模块系统。
📌 说明:本天内容严格对应官方 PDF 第 1-3 章(入门指南 + 常见编程概念)。闭包、接口与虚表、泛型、async/await 等高级概念将在对应章节正式讲解,本天仅在”扩展参考”部分做概念预告。
课前小故事:编程语言的”底层逻辑”
你有没有想过,为什么有些人学新语言特别快?因为他们掌握了编程的底层逻辑。
无论你用什么语言——C、Java、Python 还是 Rust——底层都绕不开一组核心概念:数据怎么存(值与类型)、代码怎么组织(函数与方法)。
编程语言,不过是这些概念的具体表述和载体。今天我们在 Rust 的语境下,先把最基础的概念讲透。后面学到所有权、生命周期、Trait 等难点时,你会发现它们不过是今天讲的概念的延伸。
📌 今天的内容对应官方 PDF 第 1-3 章。更高级的概念(闭包、泛型、接口、异步等)会在后续对应天数正式讲解。
RustRover 操作指引
创建第 2 天项目
- 点击菜单
File → New Project - 选择
Rust,项目名填day02_concepts,模板选Binary - 点击
Create
快速验证表达式
写 Rust 时经常想快速验证某个表达式:
1. Scratch File(临时草稿文件)
- 按
Ctrl+Alt+Shift+Insert - 或菜单选择
File → New → Scratch File → Rust - 临时文件不会保存到项目中,适合随手试验
2. 悬停查看类型
- 把鼠标悬停在变量名上
- RustRover 会弹出提示框,显示变量类型和当前值
3. Evaluate Expression(调试时)
- 先设置断点(行号左侧点击出现红色圆点)
Shift+F9调试运行- 程序停在断点后,
Alt+F8打开求值窗口
常用编辑快捷键
| 快捷键 | 作用 |
|---|---|
Ctrl+Alt+Shift+Insert |
新建 Scratch File |
Ctrl+B |
跳转到定义 |
Ctrl+/ |
注释/取消注释 |
Ctrl+D |
复制当前行 |
Ctrl+Shift+F10 |
运行当前文件 |
Shift+F9 |
调试运行 |
Alt+F8 |
调试时求值表达式 |
Ctrl+Shift+A |
搜索任意动作 |
常见错误提示
cannot assign twice to immutable variable- 原因:对不可变变量重新赋值
- 解决:加
mut或用新的let
mismatched types- 原因:变量类型和赋值的类型不一致
- 解决:检查类型标注
expected type, found {integer}- 原因:整数字面量类型不明确
- 解决:加上类型标注,如
let x: u32 = 5;
语法讲解
💡 初学者提示:这一节概念较多,如果一遍看不懂没关系。后面的 Day 3-4 会用具体代码再次演示这些概念。先把”值有类型、类型决定大小”这个核心记住。
一、Cargo 基础深入
Cargo 是 Rust 的”瑞士军刀”,身兼数职。它类似 Node.js 的 npm、Golang 的 go,用来做依赖管理以及开发过程中的任务管理。
Cargo 项目结构
1 | day02_concepts/ |
Cargo.toml 详解
1 | [package] |
💡 类比理解:
Cargo.toml就像一份餐厅的菜单和营业执照。菜单(dependencies)告诉顾客能吃到什么菜,营业执照(package)说明你叫什么名字、是什么版本。一个 Rust 项目也被称为一个 crate。
Rust 项目的组织结构
当 Rust 代码规模越来越大时,可以用 mod 来组织代码。
- crate:一个项目就是一个 crate,可以是可执行项目,也可以是库(用
cargo new --lib创建) - mod:用
mod在入口文件中声明要加载的其它代码文件 - workspace:当代码规模继续增长,一个 workspace 可以包含多个 crates,只有涉及的 crate 需要重新编译
单元测试与集成测试
1 | // 单元测试:和被测代码放在同一个文件中 |
- 单元测试:放在和被测代码相同的文件中,使用
#[cfg(test)]条件编译 - 集成测试:放在
tests目录下(与src平行),只能测试 crate 的公开接口 - 运行测试:
cargo test
二、值与类型
类型是对值的区分
严谨地说,类型是对值的区分,它包含了值在内存中的长度、对齐以及值可以进行的操作等信息。一个值是符合某个特定类型的数据实体。
比如 64u8,它是 u8 类型,对应一个字节大小、取值范围在 0~255 的某个整数实体,这个实体是 64。
⚠️ 关键理解:值无法脱离具体的类型讨论。同样是内存中的一个字节
0x40,如果其类型是u8,那么含义是 64;如果其类型是 ASCIIchar,那么含义是@符号。
原生类型与组合类型
- 原生类型:编程语言提供的最基础的数据类型。所有原生类型的大小都是固定的,因此它们可以被分配到栈上。
- 结构体:多个类型组合在一起共同表达一个值的复杂数据结构。比如
User结构体包含 name、age、email。 - 标签联合:可以存储一组不同但固定的类型中的某个类型的对象,具体是哪个类型由其标签决定。Rust 中的
enum就是标签联合。
Rust 中的基本数据类型
1 | fn main() { |
三、指针与引用
在内存中,一个值被存储到某个位置,这个位置对应一个内存地址。
- 指针:一个持有内存地址的值,可以通过解引用(dereference)来访问它指向的内存地址,理论上可以解引用到任意数据类型。
- 引用:和指针类似,但解引用访问是受限的,只能解引用到它引用数据的类型,不能用作它用。
⚠️ 安全提示:指针的使用限制更少,但也会带来更多危害。如果没有用正确的类型解引用一个指针,会引发内存问题。引用更安全,Rust 大量使用引用而非裸指针。
胖指针(Fat Pointer)
某些引用除了需要指针指向内存地址外,还需要额外的元数据。比如指向字符串切片的引用 &str 是「指针 + 长度」共 2 个 word(64 位下 16 字节);而 String 是「指针 + 长度 + 容量」共 3 个 word(24 字节)。这种比普通指针携带更多信息的指针,称为胖指针。
四、语句与表达式
Rust 新手最容易混淆的概念之一:
- 语句(Statement):执行一个动作,没有返回值,以分号
;结尾 - 表达式(Expression):计算并产生一个值,不以分号结尾
1 | fn main() { |
💡 类比理解:语句就像给厨师下的命令——“切洋葱”、”开火”;表达式就像一道菜——厨师做完后端给你(返回一个值)。函数最后一行如果是表达式,就是把这道菜端给调用者。
1 | // 返回值是表达式 vs 隐式返回 unit |
方法
在面向对象的编程语言中,在类或对象中定义的函数被称为方法。方法往往和对象的引用发生关系,比如 Python 对象的 self,或 Java 对象的 this。
📌 扩展参考(超出本天范围,后续正式讲解)
以下内容为概念预告,帮助你在后续学习中有心理准备。不需要现在就掌握,了解概念即可。正式讲解和练习在对应的天数。
五、闭包(详见 Day 19)
闭包是将函数、或者说代码和其环境一起存储的一种数据结构。闭包引用的上下文中的自由变量,会被捕获到闭包的结构中,成为闭包类型的一部分。
1 | fn main() { |
💡 类比理解:函数就像一份打印好的菜谱,谁都能照着做;闭包就像一份装了”秘密调料”的菜谱——调料(自由变量)已经打包在菜谱里了,不管谁拿到这份菜谱,用的都是同一份调料。
六、接口与虚表(详见 Day 14/17/18)
📌 概念预告:接口是软件设计的核心抽象层。Rust 用
trait来实现接口概念。虚表是实现运行时多态的底层机制。这些内容将在 Day 14(模块系统 + trait 初识)、Day 17(泛型与 trait 深入)、Day 18(Trait Object 与动态分发)中正式讲解。
接口是一个软件系统开发的核心部分,它反映了系统设计者对系统的抽象理解。作为一个抽象层,接口将使用方和实现方隔离开来,使两者不直接有依赖关系,大大提高了复用性和扩展性。
很多编程语言都有接口的概念:Java 的 interface、Swift 的 protocol、Rust 的 trait。
虚表(Virtual Table)
当我们在运行期使用接口来引用具体类型的时候,变量原本的类型被抹去。因此需要构建胖指针,除了指向数据本身外,还需要指向一张涵盖了这个接口所支持方法的列表——这就是虚表。
七、并发与异步(详见 Day 26-28)
📌 概念预告:async/await 是 Rust 处理异步 I/O 的核心语法。这些内容将在 Day 26(并发处理)、Day 27(Future 与 async/await)、Day 28(异步 IO 与网络开发)中正式讲解。
操作系统为应用程序提供了异步 I/O,让应用可以在当前 I/O 处理完毕之前,将 CPU 时间用作其它任务的处理。
Promise / async / await
异步处理完成后的结果,一般用 Promise(也叫 Future/Delay/Deferred)来保存。它代表在未来的某个时刻才能获得的结果的值,存在三个状态:
- 初始状态:Promise 还未运行
- 等待(pending)状态:Promise 已运行,但还未结束
- 结束状态:成功解析出一个值,或者执行失败
async 定义了一个可以并发执行的任务,await 触发这个任务并发执行。大多数语言中,async/await 是一个语法糖,它使用状态机将 Promise 包装起来,让异步调用的使用感觉和同步调用类似。
八、泛型编程(详见 Day 17)
📌 概念预告:泛型是 Rust 实现代码复用的核心机制。将在 Day 17(泛型与 trait 深入)中正式讲解。
数据结构的泛型
泛型编程包含两个层面。首先是数据结构的泛型(参数化类型),比如:
1 | // 这是一个泛型结构体,S 是类型参数 |
参数 S 的具体类型只有在使用 Connection 的上下文中才得到绑定。你可以把参数化数据结构理解成一个产生类型的函数——“调用”时接受具体类型参数,返回携带这些类型的类型。
💡 类比理解:泛型就像一个”万能模具”。你造了一个
Connection<S>的模具,当你需要 TcpStream 类型的连接时,就把 S 替换成 TcpStream,得到Connection<TcpStream>。
但问题来了:如果 S 可以是任意类型,怎么知道 S 有什么行为?这就需要用接口对 S 进行约束。所以支持泛型编程的语言,必然提供强大的接口编程能力。
代码的泛型化
泛型编程的另一个层面是使用泛型结构后代码的泛型化。比如二分查找,用 C 写只能针对 int[],换一种数据类型就要重写;而用 C++ 的模板/迭代器,同一套代码可以适用于多种数据类型。
泛型编程带来的好处是延迟绑定,让数据结构通用性更强;也大大减少代码重复,提高可维护性。
常见错误 / 避坑指南
1. 混淆语句和表达式
1 | // 错误:最后加了分号,变成语句,返回 unit 而非 i32 |
2. 函数参数类型不明确
1 | // 错误:Rust 无法推断 x 的类型 |
3. 混淆并发和并行
记住:并发是”同时处理多件事的能力”(CPU 时间片轮转),并行是”同时做很多件事”(多核 CPU 同时执行)。
💡 类比理解:并发就像一个厨师同时做几道菜——先切菜,趁炖汤的时候去炒另一个菜;并行就像多个厨师同时做不同的菜。
🔒 安全视角延伸
依赖安全:你的供应链就是你的攻击面
Rust 的 Cargo 生态系统让引入第三方库变得异常简单——一行 Cargo.toml 就能添加一个 crate。但便利的背后隐藏着一个严肃的安全问题:你依赖的每一个 crate 都是潜在攻击面。
为什么依赖是安全隐患?
一个典型的 Rust 项目可能直接依赖 10-20 个 crate,但加上间接依赖(依赖的依赖),总数可能高达数百个。这些 crate 中任何一个如果包含漏洞或恶意代码,都可能被攻击者利用。这就是所谓的供应链攻击(Supply Chain Attack)。
例如,2024 年安全研究人员发现多个恶意 npm/PyPI 包仿冒流行库名称进行钓鱼。Rust 生态虽然相对年轻,但并非免疫——crate 注册表是开放的,任何人都可以发布包。
cargo-audit:扫描已知漏洞
Rust 社区提供了 cargo-audit 工具,它会对你的 Cargo.lock 进行检查,比对 RustSec Advisory Database 中已公开的漏洞数据库:
1 | # 安装 cargo-audit 工具 |
运行后,cargo audit 会输出类似下面的报告:
1 | CARGO_AUDIT_REPORT |
报告会告诉你哪个 crate 的哪个版本存在漏洞、严重程度、修复版本。你只需在 Cargo.toml 中升级到安全版本即可。
Cargo.lock:锁定版本的重要性
Cargo.lock 文件记录了每个依赖的确切版本号。不要随意执行 cargo update——它可能会把某个 crate 升级到一个你尚未审查的新版本。在团队协作中,Cargo.lock 应该被提交到版本控制,确保所有成员使用完全相同的依赖版本。
💡 类比理解:
Cargo.lock就像餐厅的采购清单——上面写明了每个供应商的批次号。如果某个批次出了问题,你可以精确地追溯和替换。没有这份清单,你每次买到的食材可能都不一样,出了问题很难排查。
cargo-deny:许可证与来源审查
除了漏洞,还需要关注依赖的许可证合规性和来源可信度。cargo-deny 可以检查:
1 | # 安装 cargo-deny |
配置文件 deny.toml 示例:
1 | # 💡 unsafe 是 Rust 的"逃生舱",允许绕过安全检查。将在 Day 25 详细学习。 |
安全实践清单
- 定期运行
cargo audit:建议集成到 CI/CD 流水线中 - 提交
Cargo.lock:确保团队依赖版本一致 - 谨慎
cargo update:升级前查看 changelog - 使用
cargo deny:检查许可证和来源 - 审查新依赖:添加新 crate 前查看其维护状态和下载量
📖 延伸阅读:RustSec Advisory Database — Rust 生态系统的安全公告数据库,可以订阅 RSS 获取最新漏洞通告。
课后实验作业
作业 1:函数作为一等公民
- 编写函数
apply(value: i32, f: fn(i32) -> i32) -> i32 - 实现
square和cube两个函数传入apply测试 - 用
println!打印结果
作业 2:语句与表达式练习
- 编写函数,分别返回
f64和()(unit),体会表达式和语句的区别 - 使用代码块
{}作为表达式赋值给变量
作业 3:模块组织练习
- 创建一个新项目,在
src/main.rs中使用mod引入一个math_utils模块 - 在
math_utils.rs中定义add和multiply函数 - 在
main中调用这两个函数
作业 4(大实验种子):Agent 配置常量
- 在
pi_agent的main.rs中定义常量:1
2
3const AGENT_NAME: &str = "Pi Agent";
const DEFAULT_TEMPERATURE: f64 = 0.7;
const MAX_HISTORY: usize = 10;
1 | - 在 `main` 中打印这些常量 |
作业 2 答案
1 | // day02_concepts/src/main.rs |
作业 3 答案
1 | // day02_concepts/src/math_utils.rs |
1 | // day02_concepts/src/main.rs |
作业 4 答案(大实验种子)
1 | // pi_agent/src/main.rs |
今日小结
- Cargo 是 Rust 的项目管理工具,类似 npm/go,管理依赖和构建流程。
- 值无法脱离类型讨论;类型分为原生类型(固定大小、栈上分配)和组合类型(结构体、标签联合)。
- 指针持有内存地址,引用是受限的指针——Rust 优先使用引用保证安全。
- 胖指针除了地址还携带长度等信息,Rust 中很多数据结构引用都是胖指针。
- 函数是一等公民,可以作为参数和返回值。(闭包将在 Day 19 正式讲解)
- 语句执行动作返回
(),表达式产生值——Rust 绝大多数代码都是表达式。 - 接口隔离使用方和实现方,虚表是运行时多态的基础。
- 并发是能力,并行是手段;同步阻塞,异步不阻塞。
- 泛型编程让数据结构像函数一样延迟绑定类型参数,提升通用性。
- 今天我们给
pi_agent添加了配置常量,明天深入内存的堆与栈。




