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