编程进阶:Rust
2025-08-01 10:40:10 1 举报
AI智能生成
Rust是一门高性能开发语言,适合高效网关服务,计算服务,底层接口,内存优化等场景。 先介绍rust的数据结构和控制流,已经常用入门,面试总结。 然后讨论 所有权(Ownership) Rust 最核心机制,变量默认是独占的,赋值会转移所有权。 借用(Borrowing) &T 是只读借用,&mut T 是可变借用。一次只能有一个可变借用。 生命周期(Lifetimes) 编译器通过生命周期参数(如 <'a>)防止悬垂引用。
作者其他创作
大纲/内容
Rust 入门
rust介绍
Rust 是一种由 Mozilla 开发、强调安全性、并发性和性能的系统级编程语言。
Rust 的特点
内存安全(Memory Safety)
使用所有权(Ownership)、借用(Borrowing)、生命周期(Lifetimes)机制代替垃圾回收。
编译器在编译期间强制检查,防止空指针、悬垂引用、数据竞争等问题。
编译器在编译期间强制检查,防止空指针、悬垂引用、数据竞争等问题。
并发安全(Fearless Concurrency)
编译器帮助程序员在编译期发现潜在的数据竞争问题。
线程安全是语言设计的一部分。
线程安全是语言设计的一部分。
无垃圾回收(No Garbage Collector)
没有 GC,提高运行时性能,适合系统编程和嵌入式开发。
零成本抽象(Zero-cost Abstractions)
提供高级抽象时不引入额外运行时开销。
现代语法与工具链
强大的构建工具和包管理器(Cargo)。
集成测试、文档、依赖管理一体化。
集成测试、文档、依赖管理一体化。
**跨平台性**
支持多种平台(Windows、Linux、macOS、WebAssembly、嵌入式)
**社区活跃**
有稳定增长的生态(如 tokio、actix、serde、wasm-pack)。
Rust 的缺点
学习曲线 所有权、借用、生命周期机制对新手不友好,需要时间理解。
编译速度 编译器非常严格,导致编译速度相对较慢。
生态成熟度 虽然发展迅速,但某些领域(如 GUI、游戏开发)生态不如 C++ 或 Java 成熟。
rust安装
注意整个rust的组件,工具链,以及提示的通常的环境变量
Linux 安装
使用 curl 命令行
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
提示几个选项,默认走继续流程就好,安装成功后会安装组件rustc, cargo
rustc --version
rustc 1.87.0 (17067e9ac 2025-05-09)
rustc 1.87.0 (17067e9ac 2025-05-09)
cargo --version
cargo 1.87.0 (99624be96 2025-05-06)
cargo 1.87.0 (99624be96 2025-05-06)
重复安装的时候会提示已近下载downloader
Windows 安装
下载官方安装器 https://rustup.rs 提供的 .exe 安装包。
或者使用 PowerShell 命令
Invoke-WebRequest -Uri https://sh.rustup.rs -UseBasicParsing | Invoke-Expression
入门案例
从官网下载 .deb 包:https://code.visualstudio.com
sudo dpkg -i code_1.101.1-1750254731_amd64.deb
安装插件
Rust Analyzer
CodeLLDB
使用_和rs来编写源代码文件
使用toml来编写配置文件
rustc
简单的编译工具
rustc source.rs
安装rustup后自带该工具
cargo
很强的编译工具
cargo new
创建项目
cargo new hello_cargo
进入项目
cd hello_cargo
code .
cargo check
检测编译,速度很快,不需要实际编译
猜数字游戏案例
使用cargo创建项目
编写核心代码
编译检测,编译运行
优化代码逻辑
命名规范(Naming Conventions)
类型 命名方式 示例
变量 / 函数 snake_case let my_variable; fn do_something()
类型 / 结构体 PascalCase struct MyStruct; enum MyEnum
常量 SCREAMING_SNAKE_CASE const MAX_VALUE: u32 = 100;
包名 / crate 名 snake_case 在 Cargo.toml 中:name = "my_crate"
变量 / 函数 snake_case let my_variable; fn do_something()
类型 / 结构体 PascalCase struct MyStruct; enum MyEnum
常量 SCREAMING_SNAKE_CASE const MAX_VALUE: u32 = 100;
包名 / crate 名 snake_case 在 Cargo.toml 中:name = "my_crate"
注释规范(Comments)
单行注释使用 //
多行注释使用 /* ... */
文档注释使用 ///(支持 Markdown)
多行注释使用 /* ... */
文档注释使用 ///(支持 Markdown)
数据类型
标量类型(Scalar Types)
i8~i128, u8~u128 区分有符号/无符号;
isize/usize
f32, f64 默认 f64,适合科学计算。
bool 布尔类型,仅 true 和 false。
char 使用 Unicode 字符(4 字节),不是 ASCII 字符。
数组(Array)
✅ 类似传统语言的Array,开辟固定长度的连续内存。
let arr: [i32; 4] = [1, 2, 3, 4];
println!("arr[{}] = {}", 1, arr[1]);
println!("arr[{}] = {}", 1, arr[1]);
栈上分配、性能高
不可混不同类型
不能动态扩容
固定长度 [T; N],访问用[index]取值
元组(Tuple)
✅ 类似 Python 的 tuple,但静态类型更强。
let tup: (i32, f64, char) = (100, 6.28, '💡');
println!("Access by index: {}, {}, {}", tup.0, tup.1, tup.2);
println!("Access by index: {}, {}, {}", tup.0, tup.1, tup.2);
存储位置: 默认在栈上(当元素都是栈上的类型)
内存布局: 元组是固定大小、连续内存的复合类型。
可混合不同类型:(i32, f64, char)
不能动态扩容
长度固定,访问用 .0, .1 或解构
常用于函数多值返回
字符串(String 和 &str)
⚠️ 注意:Rust 字符串是 UTF-8 编码,不能直接通过索引访问字符,需用 .chars() 或 .get()。
let slice: &str = "Rust slice";
let mut s: String = String::from("Rust String");
s.push_str(" is cool");
let mut s: String = String::from("Rust String");
s.push_str(" is cool");
&str 字符串切片,通常是引用文字 "hello"
不可变(默认)栈上分配
是一个指向字符串内容的引用
通常是静态生命周期
是一个指向字符串内容的引用
通常是静态生命周期
内存小(指针+长度)
只读、性能敏感场合
String 可变的堆分配字符串,可动态增长
可变的、堆分配的字符串类型
拥有自己内存中的数据
可以被 push、pop、修改等
拥有自己内存中的数据
可以被 push、pop、修改等
内存占用大(包含堆数据)
需要频繁修改的字符串
集合类
向量(Vec<T>)
✅ 常用于动态存储数据。最常见的集合类型。
内部是堆分配,支持 push, pop, sort
使用Vec::from([1, 2, 3])
常用简写 vec![1, 2, 3] 创建
let mut vec = vec![10, 20, 30];
vec.push(40);
vec.push(40);
类似动态数组,可变、自动扩容
哈希映射(HashMap<K, V>)
⚠️ 默认哈希函数偏重安全,若追求速度可用 FxHashMap 替代。
存储位置: 元数据在栈上,键值对存储在堆上。
自动管理内存(使用 Drop 释放堆内数据)
键值对的所有权也会被移动到 map 中
键值对集合,需引入 use std::collections::HashMap;
let mut map: HashMap<String, i32> = HashMap::new();
map.insert("apple".to_string(), 5);
map.insert("apple".to_string(), 5);
插入性能好,键必须可哈希(实现 Hash + Eq)
集合(HashSet<T>)
✅ 强大的代数数据类型(ADT)系统,适合表达业务状态和错误处理
struct 用于定义自定义数据模型
enum 用于可变状态(如 Option, Result)
数据内存核心
✅ Move(默认行为)
拷贝数据变量在 栈 上的指针
高效
会致使原变量失效。
只在栈上处理指针
let s1 = String::from("hello");
let s2 = s1; // move
// s1 不能再用
let s2 = s1; // move
// s1 不能再用
✅ Copy(默认在实现了Copy类型)
直接拷贝 栈 上的数据
高效
不会使原变量失效。
只为新的变量在栈上添加指针
let x = 5;
let y = x; // 自动 Copy
栈上分配的简单类型(如整数、布尔、浮点、char、数组 [T; N])默认 Copy。
let y = x; // 自动 Copy
栈上分配的简单类型(如整数、布尔、浮点、char、数组 [T; N])默认 Copy。
✅ Clone (显式深拷贝)
克隆如深拷贝,拷贝堆内存的实际数据,也要创建新的指针
成本高
原来的变量不变
let s1 = String::from("hi");
let s2 = s1.clone(); // 深拷贝
调用 .clone() 会在堆上复制实际数据,性能成本高。
let s2 = s1.clone(); // 深拷贝
调用 .clone() 会在堆上复制实际数据,性能成本高。
使用场景:你还想保留原变量,且值不小。
浅拷贝的方式
使用Rc / Arc clone
let a = Rc::new(String::from("hello"));
let b = Rc::clone(&a); // ✔️ 浅拷贝,共享同一堆内存
let b = Rc::clone(&a); // ✔️ 浅拷贝,共享同一堆内存
内存回优化
所有权(Ownership)
Rust 最核心的内存安全机制,无需 GC。
所有权三原则
每个值在某一时刻只有一个所有者。
当所有者离开作用域,其值会被释放(调用 drop)。
移动(move)后,原变量失效。
实际核心优点
在编译期强制内存安全
避免未释放内存,导致内存泄露
释放的内存指针却存在,悬垂指针
指针指向的对象已经被释放(dangling pointer)。野指针
替代 GC 的开销和不确定性
常规语法操作
循环案例
while
while bool {}
loop
let v = loop {}
for
for obj in iters
迭代器
.iter(), .iter_mut(), .into_iter() 是 Rust 中创建迭代器的三种最常用方式。它们主要区别在于 元素类型(引用或值),所有权(ownership)
.iter():不可变引用迭代器
不消耗原集合的所有权
每次迭代产生 &T(不可变引用)
适用于只读遍历
每次迭代产生 &T(不可变引用)
适用于只读遍历
.iter_mut():可变引用迭代器
不消耗原集合的所有权
每次迭代产生 &mut T(可变引用)
可用于修改集合中的元素
每次迭代产生 &mut T(可变引用)
可用于修改集合中的元素
.into_iter():值迭代器
消耗集合的所有权
每次迭代产生 T(值本身)
常用于移动值、所有权转移、转成其他结构
每次迭代产生 T(值本身)
常用于移动值、所有权转移、转成其他结构
Adopt 适配器
迭代器适配器允许你通过方法链来改变或过滤迭代器的内容,而不会立刻消耗它。
常见适配器
map():对每个元素应用某个函数,并返回一个新的迭代器。
filter():过滤出满足条件的元素。
take(n):只返回前 n 个元素的迭代器。
skip(n):跳过前 n 个元素,返回剩下的元素迭代器。
chain() 连接多个迭代器。 let combined = (1..3).chain(4..6).collect::<Vec<_>>()
消耗型适配器
使用迭代器直到它被完全消耗。
collect():将迭代器转换为集合(如向量、哈希集)。
sum():计算迭代器中所有元素的和。
product():计算迭代器中所有元素的乘积。
count():返回迭代器中元素的个数。
all() 如果迭代器中的所有元素都满足某个条件,返回 true。 let all_positive = (1..=5).into_iter().all(|x| x > 0);
any() 如果迭代器中的至少一个元素满足某个条件,返回 true。 let any_negative = (1..5).into_iter().any(|x| x < 0);
find() 返回迭代器中第一个满足某个条件的元素。 let first_even = (1..10).into_iter().find(|x| x % 2 == 0);
错误处理
推荐使用 Result 和 Option 而非异常。
使用 ? 运算符简化传播错误。
对于不可恢复错误使用 panic!(但要谨慎)。
Cargo 管理
环境的安装
安装包
Cargo.toml 文件下管理包名和版本
指定版本
Cargo.lock 会在第一次build的时候会把版本记录下
重新build的时候cargo会核对Cargo.lock
cargo update
忽略cargo.lock,更新注册表crates.io
很强的编译工具
cargo new
创建项目
cargo new hello_cargo
进入项目
cd hello_cargo
code .
cargo run
编译项目 + 运行项目入口
cargo run hello_cargo
cargo build
编译项目
cargo build
debug
cargo build --release
release
编译更耗时,编译代码更加高效
cargo check
检测编译,速度很快,不需要实际编译
包管理工具
cargo 的惯例
一个Package 可以同时包含 src/main.rs 和 src/lib.rs
一个是binary crate
一个是library crate
名称都和 Package 相同
一个Package 可以有多个 binary crate
每个文件是单独的binary crate
module 控制作用域
Module 的设计
在一个 crate 中将代码进行分组
增加项目的可读性,复用性
src/main.rs 和 src/lib.rs 任意一个形成了名为crate root
控制项目中的 public 和 private 属性
把 struct, enum 可以放到某个模块中,这样将其设为私有
Module 的建立
mod 是关键字,可以嵌套
可以包含其他项 - fn, struct, enum, trait
典型的项目结构
典型的 Rust 项目案例
project/
├── Cargo.toml
├── src/
│ ├── main.rs (如果是 binary crate)
│ ├── lib.rs (如果是 library crate)
│ └── *.rs 或 modules/
├── tests/ # 测试文件
├── examples/ # 示例代码
└── benches/ # 基准测试
├── Cargo.toml
├── src/
│ ├── main.rs (如果是 binary crate)
│ ├── lib.rs (如果是 library crate)
│ └── *.rs 或 modules/
├── tests/ # 测试文件
├── examples/ # 示例代码
└── benches/ # 基准测试
包管理规范(Crate Management)
每个 crate 应该只做一件事。
避免创建“万能”库,鼓励职责单一。
合理划分 lib.rs 和 main.rs。
使用 #[cfg] 属性进行条件编译。
避免创建“万能”库,鼓励职责单一。
合理划分 lib.rs 和 main.rs。
使用 #[cfg] 属性进行条件编译。
Rust 进阶优化
先聊聊常见内存问题
内存泄漏(Memory Leak)
程序在运行过程中动态分配了内存,但使用完毕后没有释放,导致内存被占用。
常见原因
忘记调用 free() / delete。
数据结构未正确清空(如链表、哈希表、缓存等)。
循环引用(在有 GC 的语言中也可能发生)。
内存溢出(Out of Memory, OOM)
程序尝试申请超过可用内存的资源,导致系统拒绝分配。
常见原因
内存泄漏累积。
动态集合,缓存无上限增长。
不合理的大对象申请(如一次性加载大文件)。
多线程/递归深度过大导致栈溢出。
野指针(Wild Pointer)
指向一个不确定的内存地址的指针,访问它会导致不可预测的行为。
常见原因
指针未初始化就使用。
程序崩溃或出现随机错误。
指针指向的对象已经被释放(dangling pointer)。
可能出现段错误(Segmentation Fault)。
避免方式
初始化指针为 NULL。
释放后置指针为 NULL。
使用智能指针(C++)、ARC(Objective-C)或 GC(Java)。
悬空指针(Dangling Pointer)
指针所指向的内存已被释放,但指针本身仍保留该地址值。
有时可写入,但行为不可预测。
解决办法
使用后置 NULL。
使用 RAII 或智能指针管理生命周期。
栈溢出(Stack Overflow)
调用栈深度太大,超出栈空间限制。
常见原因
无限递归。
出现 "Segmentation fault" 或 "Stack overflow" 错误。
局部变量占用太多栈空间。
解决方案
用循环代替递归。
避免大数组在函数内定义。
内存回优化
传统的老牌语言
C/C++ 中的 malloc/free手动回收
不需要的内存手动drop()
未处理的变量导致内存泄漏(Memory Leak)
特点
程序员可以精确决定什么时候分配或释放内存,避免了 GC 的“停顿”或延迟。
在对性能敏感的应用(如游戏引擎、实时系统、嵌入式系统)中非常关键。
内存使用更可控,可以更精细地管理内存布局和生命周期
局限点
容易出错:内存泄漏(忘记释放)、悬空指针(访问已释放内存)、重复释放等问题常见。
开发效率低:开发者需花费大量时间关注内存细节,难以专注于业务逻辑。
现代编程语言
Java、Python、Go 中广泛采用自动垃圾回收机制(GC)
gc.garbage属性可以查看被标记为循环引用的对象列表
特点
相对来说对程序员更加方便
减少内存错误的概率
局限性
任然又内存问题出现的可能
引用计数,分代回收,缓存技术会影响程序运行的效率
各类管理的开销
所有权(Ownership)
Rust 最核心的内存安全机制,无需 GC。
所有权三原则
每个值在某一时刻只有一个所有者。
当所有者离开作用域,其值会被释放(调用 drop)。
移动(move)后,原变量失效。
实际核心优点
在编译期强制内存安全
避免未释放内存,导致内存泄露
释放的内存指针却存在,悬垂指针
指针指向的对象已经被释放(dangling pointer)。野指针
替代 GC 的开销和不确定性
Rc<T>:引用计数的智能指针(单线程共享所有权)
🌟 Rc 设计目的
Rust 的所有权模型默认是唯一所有权,但实际开发中:
多个对象可能需要指向同一数据(如图结构)
又不希望复制数据(Clone 会带来性能和语义问题)
多个对象可能需要指向同一数据(如图结构)
又不希望复制数据(Clone 会带来性能和语义问题)
🧩基本使用
use std::rc::Rc;
fn main() {
let data = Rc::new(String::from("Shared!"));
let a = Rc::clone(&data); // 增加引用计数
let b = Rc::clone(&data); // 再增加
println!("data: {}, count: {}", a, Rc::strong_count(&data)); // count: 3
}
fn main() {
let data = Rc::new(String::from("Shared!"));
let a = Rc::clone(&data); // 增加引用计数
let b = Rc::clone(&data); // 再增加
println!("data: {}, count: {}", a, Rc::strong_count(&data)); // count: 3
}
let a = Rc::new(String::from("hello"));
let b = Rc::clone(&a); // ✔️ 浅拷贝,共享同一堆内存
let s1 = String::from("hi");
let s2 = s1.clone(); // ❗️深拷贝,复制一份堆数据
let b = Rc::clone(&a); // ✔️ 浅拷贝,共享同一堆内存
let s1 = String::from("hi");
let s2 = s1.clone(); // ❗️深拷贝,复制一份堆数据
🚫 Rc<T> 的限制
不是线程安全的,不能在线程间共享:
let data = Rc::new(5);
std::thread::spawn(move || {
println!("{}", data); // ❌ 编译错误:Rc 不实现 Send
});
let data = Rc::new(5);
std::thread::spawn(move || {
println!("{}", data); // ❌ 编译错误:Rc 不实现 Send
});
Rc + RefCell:单线程内部可变共享
use std::rc::Rc;
use std::cell::RefCell;
let shared = Rc::new(RefCell::new(42));
let a = shared.clone();
let b = shared.clone();
*a.borrow_mut() += 1; // 修改数据
use std::cell::RefCell;
let shared = Rc::new(RefCell::new(42));
let a = shared.clone();
let b = shared.clone();
*a.borrow_mut() += 1; // 修改数据
Arc<T>:原子引用计数(线程安全共享所有权)
🌟 Arc 是“Atomic Reference Counted”缩写,几乎和 Rc 一样,只不过支持线程安全的引用计数。
🧩基本使用
use std::sync::Arc;
use std::thread;
fn main() {
let data = Arc::new("Shared across threads!");
let mut handles = vec![];
for _ in 0..3 {
let shared = Arc::clone(&data);
let handle = thread::spawn(move || {
println!("{}", shared);
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
}
use std::thread;
fn main() {
let data = Arc::new("Shared across threads!");
let mut handles = vec![];
for _ in 0..3 {
let shared = Arc::clone(&data);
let handle = thread::spawn(move || {
println!("{}", shared);
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
}
⚠️ Arc<T> 只能保证引用计数本身是安全的,数据本身如果需要修改,还需搭配 Mutex<T> 或 RwLock<T>。
✅ Arc<T> 实现了 Send 和 Sync,可在线程间共享。
生命周期('a)
生命周期确保引用在使用期间是有效的,编译器要追踪谁先死。
案例1
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() { x } else { y }
}
'a 表示两个引用必须活得一样久
编译器推导大部分常见场景,不写也行
if x.len() > y.len() { x } else { y }
}
'a 表示两个引用必须活得一样久
编译器推导大部分常见场景,不写也行
🔍什么时候需要写生命周期?
当函数返回值是引用,且涉及多个参数引用
当定义 struct 中字段是引用
优化请求
减少 HTTP 请求
一个完整的 HTTP 请求需要经历 DNS 查找,TCP 握手,浏览器发出 HTTP 请求,服务器接收请求,服务器处理请求并发回响应
使用 HTTP2
多路复用
优先级,流量控制
服务器可以主动推送
考虑一些css放头部,js放尾部
加载图片
异步
响应式
使用 webp 格式的图片
使用css3的样式图
容器化
https://www.processon.com/view/6575827967b30d6723ec0159
集群部署
https://www.processon.com/view/6575827967b30d6723ec0159
0 条评论
下一页