1 编译与执行
1.1 编译型语言特性
01.编译型语言定义
a.基本概念
Rust是编译型语言,源代码在运行前被完全编译为机器码,无需解释器或虚拟机。
b.优势
a.性能优异
直接生成机器码,运行时无额外开销。
b.早期错误检测
编译时捕获大部分错误,提高代码质量。
c.无运行时依赖
编译后的二进制文件可独立运行。
02.与其他语言对比
a.vs解释型语言
---
// Python(解释型)
def add(a, b):
return a + b
// Rust(编译型)
fn add(a: i32, b: i32) -> i32 {
a + b
}
// Rust编译后直接生成机器码,Python需要解释器
---
b.vs虚拟机语言
---
// Java编译为字节码,运行在JVM上
// Go有轻量级运行时和GC
// Rust直接编译为机器码,无GC,无运行时
---
03.编译产物
a.可执行文件
---
// 编译生成可执行文件
rustc main.rs
// 生成: main (Linux/macOS) 或 main.exe (Windows)
// 使用Cargo
cargo build
// 生成: target/debug/项目名
cargo build --release
// 生成: target/release/项目名
---
b.静态库与动态库
---
// 静态库
cargo build --lib
// 生成: target/debug/lib项目名.a (Linux/macOS)
// target/debug/项目名.lib (Windows)
// 动态库
[lib]
crate-type = ["cdylib"]
// 生成: lib项目名.so (Linux)
// lib项目名.dylib (macOS)
// 项目名.dll (Windows)
---
1.2 rustc编译器
01.rustc架构
a.编译器前端
词法分析 → 语法分析 → 语义分析 → HIR(高级中间表示)
b.编译器中端
HIR → MIR(中级中间表示)→ 借用检查 → 优化
c.编译器后端
MIR → LLVM IR → LLVM优化 → 机器码
02.基本使用
a.编译单文件
---
// main.rs
fn main() {
println!("Hello, Rust!");
}
// 编译
rustc main.rs
// 运行
./main
---
b.编译选项
---
// 优化编译
rustc -O main.rs
// 指定输出文件名
rustc main.rs -o myapp
// 显示编译过程
rustc --verbose main.rs
// 生成汇编代码
rustc --emit asm main.rs
// 生成LLVM IR
rustc --emit llvm-ir main.rs
---
03.编译器输出
a.查看中间表示
---
// 查看HIR
rustc -Z unpretty=hir main.rs
// 查看MIR
rustc -Z unpretty=mir main.rs
// 查看LLVM IR
rustc --emit llvm-ir main.rs
cat main.ll
---
b.查看汇编代码
---
rustc --emit asm main.rs
cat main.s
// 或使用cargo
cargo rustc -- --emit asm
---
1.3 LLVM后端
01.LLVM作用
a.基本概念
LLVM是Rust的编译后端,负责将MIR转换为优化的机器码。
b.优势
a.跨平台支持
支持多种CPU架构和操作系统。
b.强大优化
提供丰富的优化pass。
c.成熟稳定
被众多编译器使用,经过充分验证。
02.LLVM IR
a.中间表示
---
// Rust代码
fn add(a: i32, b: i32) -> i32 {
a + b
}
// 对应的LLVM IR(简化)
define i32 @add(i32 %a, i32 %b) {
%result = add i32 %a, %b
ret i32 %result
}
---
b.查看LLVM IR
---
rustc --emit llvm-ir main.rs
cat main.ll
---
03.目标平台
a.查看支持的目标
---
rustc --print target-list
// 输出: x86_64-unknown-linux-gnu
// aarch64-apple-darwin
// wasm32-unknown-unknown
// 等数百个目标
---
b.指定目标平台
---
// 交叉编译到Windows
rustc --target x86_64-pc-windows-gnu main.rs
// 编译到WebAssembly
rustc --target wasm32-unknown-unknown main.rs
---
1.4 编译流程:源码到机器码
01.完整编译流程
a.词法分析
源代码 → Token流
b.语法分析
Token流 → AST(抽象语法树)
c.语义分析
AST → HIR(高级中间表示)
d.类型检查
HIR → 类型推导和检查
e.借用检查
HIR → MIR → 借用检查器验证
f.优化
MIR → 优化后的MIR
g.代码生成
MIR → LLVM IR → 机器码
02.示例代码编译
a.源代码
---
fn factorial(n: u32) -> u32 {
if n == 0 {
1
} else {
n * factorial(n - 1)
}
}
fn main() {
let result = factorial(5);
println!("5! = {}", result);
}
---
b.编译过程
---
// 1. 编译并查看各阶段输出
rustc main.rs
// 2. 查看MIR
rustc -Z unpretty=mir main.rs > main.mir
// 3. 查看LLVM IR
rustc --emit llvm-ir main.rs
// 4. 查看汇编
rustc --emit asm main.rs
// 5. 生成可执行文件
rustc -O main.rs
---
03.编译时间分析
a.查看编译时间
---
cargo build --timings
// 生成编译时间报告
---
b.并行编译
---
// Cargo默认并行编译
cargo build -j 4 // 使用4个线程
---
1.5 增量编译
01.增量编译原理
a.基本概念
只重新编译修改过的代码和受影响的依赖,加快编译速度。
b.工作机制
Rust编译器跟踪代码变化,缓存未修改部分的编译结果。
02.启用增量编译
a.Cargo配置
---
// Cargo.toml
[profile.dev]
incremental = true // 默认开启
[profile.release]
incremental = true // release模式也可开启
---
b.环境变量
---
export CARGO_INCREMENTAL=1
cargo build
---
03.增量编译效果
a.首次编译
---
cargo clean
time cargo build
// 首次编译: 30秒
---
b.增量编译
---
// 修改一个文件
time cargo build
// 增量编译: 3秒
---
04.清理增量缓存
a.清理命令
---
cargo clean
// 或只清理增量缓存
rm -rf target/debug/incremental
---
1.6 交叉编译
01.交叉编译概念
a.基本定义
在一个平台上编译出另一个平台的可执行文件。
b.应用场景
在Linux上编译Windows程序、在x86上编译ARM程序等。
02.安装目标平台
a.添加目标
---
// 查看已安装目标
rustup target list --installed
// 添加Windows目标
rustup target add x86_64-pc-windows-gnu
// 添加ARM目标
rustup target add aarch64-unknown-linux-gnu
// 添加WebAssembly目标
rustup target add wasm32-unknown-unknown
---
03.交叉编译示例
a.编译到Windows
---
cargo build --target x86_64-pc-windows-gnu
// 生成: target/x86_64-pc-windows-gnu/debug/项目名.exe
---
b.编译到ARM
---
cargo build --target aarch64-unknown-linux-gnu
---
c.编译到WebAssembly
---
cargo build --target wasm32-unknown-unknown
---
04.配置链接器
a.Cargo配置
---
// .cargo/config.toml
[target.x86_64-pc-windows-gnu]
linker = "x86_64-w64-mingw32-gcc"
[target.aarch64-unknown-linux-gnu]
linker = "aarch64-linux-gnu-gcc"
---
1.7 编译优化级别
01.优化级别
a.debug模式
---
cargo build
// 优化级别: 0
// 特点: 编译快,包含调试信息,性能较低
---
b.release模式
---
cargo build --release
// 优化级别: 3
// 特点: 编译慢,高度优化,性能最佳
---
02.自定义优化
a.Cargo配置
---
// Cargo.toml
[profile.dev]
opt-level = 0 // 0-3
[profile.release]
opt-level = 3
lto = true // 链接时优化
codegen-units = 1 // 单个代码生成单元
---
b.优化级别对比
---
// opt-level = 0: 无优化,快速编译
// opt-level = 1: 基本优化
// opt-level = 2: 较多优化
// opt-level = 3: 最大优化
// opt-level = "s": 优化大小
// opt-level = "z": 极致优化大小
---
03.性能对比
a.编译时间vs运行性能
---
// debug模式
cargo build
time ./target/debug/myapp
// 编译: 5秒,运行: 10秒
// release模式
cargo build --release
time ./target/release/myapp
// 编译: 30秒,运行: 1秒
---
2 内存管理
2.1 所有权系统
01.所有权规则
a.三大规则
a.每个值都有一个所有者
---
let s = String::from("hello");
// s是字符串的所有者
---
b.同一时间只能有一个所有者
---
let s1 = String::from("hello");
let s2 = s1; // 所有权转移
// s1不再有效
---
c.所有者离开作用域时,值被丢弃
---
{
let s = String::from("hello");
} // s离开作用域,内存被释放
---
02.所有权转移
a.移动语义
---
fn main() {
let s1 = String::from("hello");
let s2 = s1; // 移动,s1失效
// println!("{}", s1); // 错误!
println!("{}", s2); // 正确
}
---
b.克隆
---
let s1 = String::from("hello");
let s2 = s1.clone(); // 深拷贝
println!("{}, {}", s1, s2); // 都有效
---
03.Copy trait
a.栈上类型
---
let x = 5;
let y = x; // 复制,x仍然有效
println!("{}, {}", x, y); // 都有效
// 整数实现了Copy trait
---
2.2 栈与堆
01.栈内存
a.特点
快速分配,自动管理,大小固定,LIFO顺序。
b.栈上数据
---
fn main() {
let x = 5; // 栈上
let y = true; // 栈上
let z = 3.14; // 栈上
}
---
02.堆内存
a.特点
动态分配,手动管理(Rust自动化),大小可变。
b.堆上数据
---
fn main() {
let s = String::from("hello"); // 堆上
let v = vec![1, 2, 3]; // 堆上
let b = Box::new(5); // 堆上
}
---
03.性能对比
a.栈分配
---
fn stack_alloc() {
let arr = [0; 1000]; // 栈上数组
}
---
b.堆分配
---
fn heap_alloc() {
let vec = vec![0; 1000]; // 堆上向量
}
---
2.3 借用检查器
01.借用规则
a.不可变借用
---
let s = String::from("hello");
let r1 = &s; // 不可变借用
let r2 = &s; // 可以有多个
println!("{}, {}", r1, r2);
---
b.可变借用
---
let mut s = String::from("hello");
let r = &mut s; // 可变借用
r.push_str(", world");
// 同一时间只能有一个可变借用
---
02.借用检查
a.编译时检查
---
let mut s = String::from("hello");
let r1 = &s;
let r2 = &mut s; // 错误!不能同时存在
---
b.生命周期
---
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() { x } else { y }
}
---
03.悬垂引用
a.编译器防止
---
fn dangle() -> &String {
let s = String::from("hello");
&s // 错误!返回悬垂引用
} // s被释放
---
2.4 生命周期
01.生命周期概念
a.基本定义
引用的有效作用域,确保引用始终有效。
b.生命周期标注
---
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() { x } else { y }
}
---
02.生命周期省略
a.省略规则
---
// 编译器自动推导
fn first_word(s: &str) -> &str {
&s[..1]
}
// 等价于
fn first_word<'a>(s: &'a str) -> &'a str {
&s[..1]
}
---
03.结构体生命周期
a.生命周期标注
---
struct ImportantExcerpt<'a> {
part: &'a str,
}
fn main() {
let novel = String::from("Call me Ishmael.");
let first_sentence = novel.split('.').next().unwrap();
let i = ImportantExcerpt {
part: first_sentence,
};
}
---
2.5 内存布局
01.数据布局
a.基本类型
---
use std::mem;
fn main() {
println!("i32: {} bytes", mem::size_of::<i32>());
println!("i64: {} bytes", mem::size_of::<i64>());
println!("&i32: {} bytes", mem::size_of::<&i32>());
}
---
b.结构体布局
---
struct Point {
x: i32,
y: i32,
}
println!("Point: {} bytes", mem::size_of::<Point>());
// 输出: 8 bytes
---
02.内存对齐
a.对齐规则
---
#[repr(C)]
struct Foo {
a: u8, // 1 byte
b: u32, // 4 bytes
c: u16, // 2 bytes
}
// 总大小: 12 bytes (含填充)
---
03.零大小类型
a.ZST示例
---
struct Unit;
println!("Unit: {} bytes", mem::size_of::<Unit>());
// 输出: 0 bytes
---
2.6 Drop trait与析构
01.Drop trait
a.自动析构
---
struct Resource {
name: String,
}
impl Drop for Resource {
fn drop(&mut self) {
println!("Dropping {}", self.name);
}
}
fn main() {
let r = Resource {
name: String::from("resource"),
};
} // 自动调用drop
---
02.手动释放
a.drop函数
---
let r = Resource {
name: String::from("resource"),
};
drop(r); // 提前释放
// r不再可用
---
03.RAII模式
a.资源管理
---
use std::fs::File;
fn main() {
let f = File::open("file.txt").unwrap();
// 使用文件
} // 文件自动关闭
---
2.7 内存安全保证
01.编译时保证
a.无空指针
Rust没有null,使用Option<T>。
b.无悬垂指针
借用检查器防止悬垂引用。
c.无数据竞争
所有权系统防止并发数据竞争。
02.运行时检查
a.边界检查
---
let v = vec![1, 2, 3];
let x = v[10]; // panic! 运行时检查
---
b.整数溢出
---
let x: u8 = 255;
let y = x + 1; // debug模式panic
---
03.unsafe代码
a.unsafe块
---
unsafe {
let p = 0x1234 as *const i32;
// 解引用原始指针
}
---
3 运行时
3.1 最小运行时
01.运行时概念
a.Rust运行时
Rust几乎没有运行时,只有最小的启动代码。
b.对比其他语言
无GC、无虚拟机、无重量级运行时。
02.启动代码
a.程序入口
---
fn main() {
println!("Hello");
}
---
03.零成本抽象
a.示例
---
let sum: i32 = (1..=100).sum();
---
3.2 程序启动流程
01.启动过程
a.入口点
操作系统 → _start → main
b.初始化
设置栈、初始化全局变量。
02.查看启动代码
a.反汇编
---
cargo build
objdump -d target/debug/myapp
---
3.3 标准库运行时
01.标准库初始化
a.全局分配器
默认使用系统分配器。
02.运行时组件
a.panic处理
---
use std::panic;
fn main() {
panic::set_hook(Box::new(|info| {
println!("Panic: {:?}", info);
}));
}
---
3.4 panic机制
01.panic触发
a.显式panic
---
panic!("Error!");
---
02.panic处理
a.捕获panic
---
use std::panic;
let result = panic::catch_unwind(|| {
panic!("test");
});
---
3.5 栈展开与abort
01.栈展开
a.默认行为
panic时展开栈,调用析构函数。
02.abort模式
a.配置
---
[profile.release]
panic = 'abort'
---
3.6 全局分配器
01.默认分配器
a.系统分配器
---
use std::alloc::System;
#[global_allocator]
static GLOBAL: System = System;
---
02.自定义分配器
a.实现GlobalAlloc
---
use std::alloc::{GlobalAlloc, Layout};
---
3.7 no_std环境
01.no_std概念
a.不使用标准库
---
#![no_std]
#![no_main]
---
02.嵌入式开发
a.目标平台
---
rustup target add thumbv7em-none-eabihf
---
4 底层机制
4.1 零成本抽象
01.零成本原则
a.定义
抽象不应带来运行时开销。
b.示例
---
let sum: i32 = (1..=100).sum();
---
02.泛型单态化
a.编译时展开
---
fn add<T>(a: T, b: T) -> T {
a + b
}
---
4.2 单态化
01.单态化概念
a.泛型展开
编译时为每个具体类型生成专门代码。
02.代码膨胀
a.二进制大小
泛型函数会为每个类型生成代码。
4.3 内联优化
01.内联标记
a.inline属性
---
#[inline]
fn add(a: i32, b: i32) -> i32 {
a + b
}
---
02.编译器优化
a.自动内联
编译器自动内联小函数。
4.4 vtable与动态分发
01.trait对象
a.动态分发
---
trait Animal {
fn speak(&self);
}
fn make_sound(animal: &dyn Animal) {
animal.speak();
}
---
02.vtable结构
a.虚函数表
trait对象包含数据指针和vtable指针。
4.5 FFI外部函数接口
01.调用C函数
a.extern声明
---
extern "C" {
fn abs(x: i32) -> i32;
}
fn main() {
unsafe {
println!("{}", abs(-3));
}
}
---
02.导出Rust函数
a.no_mangle
---
#[no_mangle]
pub extern "C" fn rust_fn(x: i32) -> i32 {
x * 2
}
---
4.6 内联汇编
01.asm!宏
a.基本使用
---
use std::arch::asm;
fn main() {
let x: u64;
unsafe {
asm!("mov {}, 5", out(reg) x);
}
println!("x = {}", x);
}
---
02.寄存器操作
a.输入输出
---
let mut x: u64 = 1;
unsafe {
asm!("add {0}, {1}", inout(reg) x, in(reg) 5);
}
---
4.7 unsafe与原始指针
01.unsafe块
a.五种unsafe操作
解引用原始指针、调用unsafe函数、访问可变静态变量等。
02.原始指针
a.创建和使用
---
let mut x = 5;
let raw = &mut x as *mut i32;
unsafe {
*raw = 10;
}
println!("x = {}", x);
---
03.安全抽象
a.封装unsafe
---
pub fn safe_fn(x: i32) -> i32 {
unsafe {
// unsafe操作
}
}
---
5 性能与调试
5.1 性能分析:perf、flamegraph
01.perf工具
a.性能采样
---
cargo build --release
perf record ./target/release/myapp
perf report
---
02.flamegraph
a.生成火焰图
---
cargo install flamegraph
cargo flamegraph
---
03.性能分析
a.热点函数
---
perf top
---
5.2 内存分析:valgrind、heaptrack
01.valgrind
a.内存检查
---
cargo build
valgrind ./target/debug/myapp
---
02.heaptrack
a.堆分析
---
heaptrack ./target/release/myapp
---
03.内存泄漏检测
a.Rust内存安全
Rust通常不会内存泄漏,除非使用Rc循环引用。
5.3 调试工具:gdb、lldb
01.gdb调试
a.基本使用
---
cargo build
gdb ./target/debug/myapp
(gdb) break main
(gdb) run
(gdb) next
---
02.lldb调试
a.macOS调试
---
lldb ./target/debug/myapp
(lldb) breakpoint set --name main
(lldb) run
---
03.调试信息
a.Cargo配置
---
[profile.release]
debug = true
---
5.4 基准测试:criterion
01.criterion使用
a.添加依赖
---
[dev-dependencies]
criterion = "0.5"
[[bench]]
name = "my_benchmark"
harness = false
---
b.编写测试
---
use criterion::{black_box, criterion_group, criterion_main, Criterion};
fn fibonacci(n: u64) -> u64 {
match n {
0 => 1,
1 => 1,
n => fibonacci(n-1) + fibonacci(n-2),
}
}
fn criterion_benchmark(c: &mut Criterion) {
c.bench_function("fib 20", |b| {
b.iter(|| fibonacci(black_box(20)))
});
}
criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);
---
02.运行基准测试
a.执行
---
cargo bench
---
5.5 编译时计算:const fn
01.const fn定义
a.编译时函数
---
const fn add(a: i32, b: i32) -> i32 {
a + b
}
const RESULT: i32 = add(2, 3);
---
02.const泛型
a.编译时泛型
---
fn array<const N: usize>() -> [i32; N] {
[0; N]
}
let arr = array::<10>();
---
03.性能优势
a.零运行时开销
编译时完成计算,运行时直接使用结果。
5.6 链接时优化:LTO
01.LTO配置
a.启用LTO
---
[profile.release]
lto = true
// 或
lto = "thin"
---
02.优化效果
a.性能提升
完整LTO: 最大优化,编译慢。thin LTO: 平衡优化和编译速度。
03.代码生成单元
a.配置
---
[profile.release]
codegen-units = 1
---
5.7 Profile-Guided Optimization
01.PGO概念
a.基于运行时数据优化
先运行程序收集数据,再用数据指导优化。
02.使用PGO
a.步骤
---
// 1. 生成instrumented版本
RUSTFLAGS="-Cprofile-generate=/tmp/pgo-data" cargo build --release
// 2. 运行程序收集数据
./target/release/myapp
// 3. 使用数据重新编译
RUSTFLAGS="-Cprofile-use=/tmp/pgo-data" cargo build --release
---
03.性能提升
a.优化效果
根据实际运行路径优化,通常可提升5-15%性能。