首页 › programming › 借了就不能改:Rust靠所有权灭七成漏洞

借了就不能改:Rust靠所有权灭七成漏洞

如果你写过 C/C++,大概率被 use-after-free、double-free、缓冲区溢出折磨过;如果你写 Java/Go/Python,那你又得忍受垃圾回收(GC)带来的停顿和内存占用。Rust 给出了第三条路:既不靠手动 free,也不靠 GC,而是靠编译器在编译期就帮你证明"这块内存永远不会被错误使用"。Let's Get Rusty 这期《Understanding Ownership in Rust》把 Rust 最核心、也最劝退的机制——所有权(Ownership)与借用(Borrowing)——用 25 分钟讲得清清楚楚。这期视频值得看,不只是因为它讲懂了一个语法点,而是因为它揭示了一种全新的思考内存的方式:把内存安全从一个"运行时赌博"变成"编译期事实"。而这一切的基石,只有一条看似不讲理的铁律:借了就不能改,改了就不能借。

视频背景卡片

项目 内容
视频标题 Understanding Ownership in Rust
频道 Let's Get Rusty
时长 约 25 分钟(章节式长教程)
播放量 35 万+(353,469 views,持续增长)
发布日期 2021 年 2 月 7 日
核心主题 所有权、移动语义、借用规则、可变引用、悬垂引用、生命周期

核心内容拆解

一、内存管理的三条赛道,Rust 选了没人走过的第四条

视频一开始并没有急着讲语法,而是先把"内存怎么管理"这件事摊开来比。主流语言的内存管理基本只有三条路:C/C++ 的手动管理(malloc/free,new/delete),效率最高但 bug 也最致命;Java/Go/Python/JS 的垃圾回收(GC),安全但代价是运行时停顿、更高的内存占用和不可预测的延迟;以及 Swift/ObjC 的引用计数(ARC/RC),介于两者之间,但有循环引用和原子操作开销。

Rust 没走任何一条老路。它靠编译器在编译期静态分析:每块内存都有一条清晰的生命轨迹,从被创建、被谁拥有、被谁借用,到最后被自动释放,全程在编译时就知道得一清二楚。运行时几乎零开销——没有 GC 线程,没有引用计数的原子操作,也没有 free 的烦恼。视频反复强调的点,也是我想强调的:Rust 的内存安全不是"跑起来才告诉你哪里错了",而是"根本不让你把会错的代码编译通过"。

二、所有权三条规则:谁拥有,谁负责清理

视频给出的所有权三条规则简单得几乎朴素:1.每个值都有一个称为"所有者"的变量;2.同一时刻只能有一个所有者;3.当所有者离开作用域,值被丢弃(drop)。这第三条是关键——Rust 用的是 RAII(资源获取即初始化)思想,作用域结束的大括号 } 一到,编译器自动插入 drop 调用。你不需要手动释放,也不会忘记释放。

这里有个反直觉的点视频讲得很透:Rust 的大括号是会"做事"的。在 GC 语言里大括号只是代码块的边界,在 Rust 里它意味着资源回收的确定性时刻。这保证了"确定性的资源释放"——文件句柄、锁、网络连接都在可预测的时间点关闭,这是 GC 语言很难做到的(GC 只管内存,不管文件锁)。

三、Move 语义:赋值为什么会让原变量"失效"

这是从其他语言转 Rust 的人第一个被绊倒的地方,也是视频着墨最多的部分。看这段代码:

let s1 = String::from("hello");
let s2 = s1;
println!("{}", s1); // 编译错误!s1 已失效

在 Python 或 Java 里,s2 = s1 只是复制了一个引用,两个变量指向同一块堆内存。但在 Rust 里,这叫移动(Move):String 在栈上存的是 (指针, 长度, 容量) 三元组,指向堆上的字符数据。如果 s1、s2 都指向同一块堆,那么两者离开作用域时都会尝试 free 同一块内存——double-free,经典的内存灾难。Rust 的解法简单粗暴:let s2 = s1 之后,s1 直接失效,所有权转移给 s2,只有 s2 能负责清理。

注意一个例外:实现了 Copy trait 的类型(整数、浮点、布尔、字符等栈上类型)赋值时是按位复制而不是移动,所以 let x = 5; let y = x; 之后 x 依然有效。视频借这个对比讲清了一个深层设计哲学:Rust 根据"复制成本"决定行为——栈上廉价类型直接复制,堆类型则移动以避免昂贵的深拷贝。这其实是一种"零成本抽象"思维:你不该为用不到的复制能力付代价。

四、借用与 XOR 铁律:本文真正的核心

如果每次用值都得把所有权搬来搬去,代码会极其笨重。于是有了借用(Borrowing):用引用 &T 借用值,不拿走所有权。但 Rust 给借用加了一条看似不讲理、实则天才的铁律:

同一时刻,你要么有一个可变引用 &mut T,要么有任意多个不可变引用 &T,两者绝不能共存。

用一句口语化的方式说就是——"借了就不能改,改了就不能借"。这条规则在学术上叫 aliasing XOR mutability(别名与可变性互斥):一块数据在任何瞬间,要么被很多人只读地共享,要么被一个人独占地修改,永远不能既被共享又被修改。这条铁律的威力在于,它一次性消灭了三类最危险的内存 bug:

  1. 数据竞争(Data Race):多线程下两个引用同时读写同一块内存?不可能了——因为不允许同时存在可变与不可变引用(配合 Send/Sync,数据竞争在编译期就被杜绝)。
  2. 迭代器失效(Iterator Invalidation):一边遍历 Vec 一边往里 push 导致底层数组重分配、迭代器悬空?编译器直接拒绝——遍历持有的是不可变借用,而 push 需要可变借用,二者不可共存。
  3. 悬垂引用(Dangling Reference):返回一个指向局部变量的引用?编译器拒绝——引用的生命周期不能超过被引用的数据。

视频把这三条串起来的方式让人豁然开朗:Rust 不是针对每个 bug 单独设计一个检查器,而是用一条统一的"别名 vs 可变性互斥"规则,把它们一网打尽。这是工程上的优雅——用一个根本性约束,消解了一整个 bug 家族。

五、七成漏洞从哪来:为什么这条铁律值钱

理解了上面这条规则,你才明白为什么整个软件行业在 2024–2026 年疯狂押注 Rust。视频点到为止的数据,我在这里补全:微软安全响应中心 2019 年披露,微软产品约 70% 的安全漏洞都是内存安全问题(Matt Miller 在 BlueHat 的演讲);Chrome 团队独立统计得到几乎相同的结论——约 70% 的严重安全 bug 是内存安全漏洞。而这些漏洞——use-after-free、缓冲区溢出、double-free、越界读写、类型混淆——几乎全部出自 C/C++,而且全部都能被 Rust 的所有权与借用规则在编译期消灭。

也就是说,Rust 这条"借了就不能改"的铁律,理论上能砍掉工业界近七成的安全漏洞。这就是为什么 NSA 在 2022 年正式建议转向内存安全语言,为什么 Linux 内核从 6.1 开始接受 Rust、并在 2024–2026 持续把驱动和文件系统用 Rust 重写,为什么 Google 报告 Android 新写的 Rust 代码内存安全漏洞数为零,为什么微软在用 Rust 重写 Windows 的核心组件(如字体、GDI 子系统),为什么 AWS 的 Firecracker、Cloudflare 的底层服务、Discord 的关键读路径(从 Go 迁到 Rust)都在用 Rust。这不是赶时髦,这是在系统性地关闭一个存在了五十年的漏洞水龙头。

六、生命周期:编译器替你追踪"引用能活多久"

视频后半段进入另一个劝退大户——生命周期(Lifetimes)。它的本质是借用规则的延伸:编译器不仅要知道"现在谁在借用",还要知道"这个借用能合法存在多久"。每个引用都有一个生命周期,表示该引用有效的范围。Rust 的 borrow checker 会确保没有任何引用能比它指向的数据活得更久。

好消息是,绝大多数时候你不用手写生命周期标注——生命周期省略规则(lifetime elision rules) 让编译器自动推断。只有在函数签名里引用之间的关系有歧义时,才需要你显式标注 'a。Rust 2018 引入的 非词法生命周期(NLL, Non-Lexical Lifetimes) 更是大幅降低了心智负担:早期 Rust 按代码词法位置(大括号)判断借用结束,会误杀很多其实安全的代码;NLL 改成按实际使用来判断,让 borrow checker 变聪明了一大截。这是 Rust 社区"驯服编译器"历程中最重要的一次改进。

七、代价:它会误杀,你要学会和它周旋

视频相对克制,没有大谈 borrow checker 的"讨厌之处",但这是每个 Rust 学习者绕不开的现实:这条铁律是保守的,它会拒绝一些其实安全的代码。你会经常和编译器"吵架",最常见的报错就是"cannot borrow X as mutable because it is also borrowed as immutable"。应对之道有几条:用 {} 缩小作用域让借用提前结束;重构数据结构拆分所有权;必要时用 clone()(虽然要付出一次深拷贝的代价);或者更高级地,用 内部可变性(Interior Mutability)——RefCell<T>(单线程,运行时检查借用规则)、Mutex<T>/RwLock<T>(多线程)、Cell<T>(Copy 类型)把借用检查从编译期挪到运行期,从而"绕过"静态铁律。这种"默认编译期严格、必要时降级到运行期"的分层设计,是 Rust 的另一层工程智慧。

八、Rust 不防什么:别神化它

最后要泼一盆冷水(也是视频评论区常见的高赞观点延伸):Rust 不是万能的安全护身符。它只防内存不安全和数据竞争,不防逻辑 bug、不防死锁(Mutex 用错了照样死锁,编译器不管)、不防整数溢出(release 模式默认是 wrapping)、不防 panic(unwrap 一个 None 照样崩溃)、更不防业务逻辑错误。Rust 给你的是"内存安全"这一层的铁饭碗,剩下的 30% 漏洞和所有逻辑问题,还得靠你自己。理解 Rust 防什么、不防什么,比单纯吹它重要得多。

知识结构图

flowchart TD
    A["Rust 所有权模型<br/>编译期内存安全"] --> B["Move 语义<br/>单一所有者"]
    A --> C["借用铁律<br/>aliasing XOR mutability<br/>借了就不能改"]
    A --> D["生命周期<br/>引用不能活过数据"]
    B --> B1["防 double-free"]
    B --> B2["确定性 drop<br/>防资源泄漏"]
    C --> C1["防数据竞争<br/>多线程"]
    C --> C2["防迭代器失效"]
    C --> C3["防悬垂引用"]
    D --> C3
    B1 & B2 & C1 & C2 & C3 --> E["编译期消除<br/>~70% 内存安全漏洞"]
    E --> F["零运行时开销<br/>无 GC 停顿 / 无 RC 原子"]
    F --> G["产业落地<br/>Linux内核·Android·Windows·AWS"]

实战启示

对普通开发者,这条视频和背后的所有权模型至少带来三层收获。

第一层,认知升级:理解了所有权与借用,你才算真正懂了"内存不是黑盒"。即使你日常写 Python/JS,理解 Rust 的模型也能让你更清楚 GC 到底在帮你兜什么底、代价是什么——为什么 Node 服务内存占用那么高、为什么 Go 的 STW 偶尔让延迟飙高。这种底层直觉,是从"会用语言"到"懂语言"的分水岭。

第二层,迁移与选型:如果你在做对延迟、对安全敏感的系统(网络面、网关、数据库、嵌入式、内核模块),Rust 已经从"实验"变成"默认选项"。通信行业的 UPF、5G/6G 核心网数据面、eBPF 工具链,越来越多的新代码用 Rust 写。学会所有权,是进入这些赛道的门票。

第三层,工程哲学:Rust 给了一个极具启发性的范式——用一条根本性的、可静态检查的约束,去消解一整类问题,而不是用一堆运行时检查去逐个修补。这种"约束即设计"的思路,完全可以迁移到你自己的系统设计里:与其在运行时反复校验不变量,不如在类型系统或编译期就把违规路径堵死。这可能是这条视频留给读者最有价值的思维模型。

学习路径上有个反直觉的建议(也是 Rust 老手的共识):别和 borrow checker 对着干。当编译器报"不能同时借用",正确反应不是堆 clone,而是退一步想"我的数据所有权结构是不是设计错了"。绝大多数 borrow checker 冲突,根因是数据组织方式不对,重构数据结构往往能让问题自然消失。

延伸学习路径

  1. 《The Rust Programming Language》("The Book")第 4 章 "Understanding Ownership" —— 视频内容的官方权威母本,免费在线,必读。
  2. Jon Gjengset 的 "Crust of Rust" 系列(YouTube)—— 工业界公认最硬核的 Rust 深度讲解,适合在掌握基础后进阶所有权与生命周期的高级用法。
  3. No Boilerplate 频道的 Rust 系列短视频—— 以极高的信息密度和剪辑节奏讲清 Rust 设计哲学,适合通勤碎片时间强化记忆。
  4. Rust By Example(doc.rust-lang.org/rust-by-example) —— 用代码而非文字学所有权,每条规则配可运行示例。
  5. 微软 Matt Miller, BlueHat IL 2019 演讲 —— "70% 漏洞是内存安全 bug"这一数据的原始出处,理解 Rust 火爆的产业根源。

一句话总结

Rust 用一条"借了就不能改"的编译期铁律,把困扰 C/C++ 五十年的内存安全 bug 在编译阶段一次性堵死——这才是它没有 GC 却能成为系统编程新王者的真正秘密。

参考来源

  1. Understanding Ownership in Rust — Let's Get Rusty — 本文分析的核心视频(35 万+ 播放)
  2. The Rust Programming Language, Ch.4 Understanding Ownership — 官方权威母本
  3. A proactive approach to more secure code — Matt Miller, Microsoft (2019) — "约 70% 漏洞是内存安全问题"数据出处
  4. NSA: Software Memory Safety (2022) — 内存安全语言推荐
  5. Rust in the Linux kernel (rust-for-linux) — Linux 内核引入 Rust 的进展与背景

本文由「51学通信」技术团队出品。51学通信由通信行业18年老兵爱卫生(微信ID: gprshome201101)维护,聚焦5G/6G核心网、3GPP标准解读、通信前沿技术深度分析。 出版作品有《5G核心网原理与实践》《IMS原理与实践》等4本通信专业书籍。知识星球「51学通信」提供深度技术答疑和行业洞察。

[!tip] 51学通信知识星球 星主:爱卫生(微信:gprshome201101)——公众号「51学通信」创办者,19 年通信行业运维与讲师经验,著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书(各大电商平台有售)。 星球定位:深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域,主打运维岗位与信令分析。 当前规模:5200 篇主题 / 990 个回答,198 元/年。 加入福利:加入星球后请加爱卫生微信,拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题,另有群内分享。 AI 加持:51学通信知识星球已作为 skill 接入 WorkBuddy,使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例,做总结、找文档和 PPT 等场景全覆盖。

51学通信知识星球海报-扫码加入