外观
Rust 简介:和 C++、Python 的区别
Rust 是一门系统编程语言。它关注的核心问题是:如何在接近 C/C++ 性能的前提下,尽量减少内存错误和并发错误。
可以这样理解 Rust:
Rust 像 C++ 一样强调性能和底层控制,但用所有权、借用和生命周期在编译期约束内存使用;它又像 Python 一样重视表达力和包管理体验,但不会牺牲静态类型和运行效率。
这也是 Rust 最特别的地方。它不是简单地站在 C++ 和 Python 中间,而是在重新回答一个老问题:既要快,又要安全,还要能长期维护,语言本身应该提供什么机制?
Rust 是什么
Rust 是一门静态类型、编译型、强调内存安全的通用编程语言。人们通常把它归到“系统编程语言”这一类,因为它可以直接编译成本地机器码,不依赖垃圾回收器,也允许程序员控制内存布局、资源释放和底层性能。
但 Rust 又不只是“更安全的 C++”。它从一开始就把工程体验放在很重要的位置:标准工具链里有 rustc 编译器、cargo 构建和包管理工具、rustfmt 格式化工具、clippy 静态检查工具。也就是说,Rust 不只提供语言语法,还提供了一套相对统一的开发流程。
Rust 适合的场景很多:
- 命令行工具;
- 网络服务;
- WebAssembly;
- 嵌入式开发;
- 数据库、编译器、浏览器组件等系统软件;
- 对性能和可靠性都有要求的基础设施代码。
Rust 的核心目标可以概括成三点:性能、安全、并发。性能来自接近 C/C++ 的编译执行模型;安全来自所有权、借用和类型系统;并发则建立在“没有数据竞争”的语言级约束之上。
Rust 的历史发展
Rust 最早是 Graydon Hoare 的个人项目,大约开始于 2006 年。后来 Mozilla 注意到这门语言,并在 2009 年左右开始赞助和推动 Rust 的开发。Mozilla 当时关心的一个重要方向,是如何构建更安全、更易并行化的浏览器基础设施;这也解释了为什么 Rust 从早期开始就非常重视内存安全和并发安全。
2010 年,Rust 项目逐渐公开化。此后几年里,这门语言经历了大量设计调整,包括所有权系统、借用检查、trait、模式匹配、模块系统、宏系统等核心机制的打磨。这个阶段 Rust 变化很快,很多早期写法后来都被修改或删除。
2015 年,Rust 1.0 发布。这是 Rust 发展史上非常重要的节点,因为它意味着语言进入稳定阶段:核心语法和标准库开始承诺向后兼容,开发者可以更放心地把 Rust 用在长期项目里。也是从这个时期开始,Rust 的学习资料、第三方库和真实工程案例开始快速增加。
后来 Rust 通过 edition 机制继续演进。edition 可以理解为对语言风格和语法习惯的一次阶段性整理,比如 Rust 2018、Rust 2021。它们允许语言改进默认写法,同时尽量不破坏旧代码。这一点和很多语言“大版本升级就可能大面积不兼容”的方式不同,Rust 更强调平滑迁移。
近些年,Rust 的应用范围已经明显扩展。它不再只是一门小众系统语言,而是被用于命令行工具、区块链基础设施、云原生组件、嵌入式系统、操作系统实验、WebAssembly 应用,以及一些大型项目中的性能关键模块。Linux 内核也开始引入 Rust 作为可选的开发语言之一,这说明 Rust 在系统编程领域已经获得了相当严肃的关注。
政策与产业背景
Rust 这几年越来越常被讨论,还有一个重要原因,就是软件安全政策开始明显关注“内存安全”问题。过去大量高危漏洞都和越界访问、悬垂指针、use-after-free 这类问题有关,而这些问题恰好长期集中出现在 C 和 C++ 这类内存不安全语言中。
美国政府近年的网络安全政策,多次强调应当推动 memory-safe languages,也就是内存安全语言。Rust 因为既有接近 C/C++ 的性能,又能在编译期约束内存使用,因此经常被当作代表性方案提到。
不过,更准确的说法是:政策层面的重点是推动更安全的软件开发路线,而不是简单地要求所有软件都必须改用 Rust。换句话说,关注点主要在于:
- 减少内存安全漏洞;
- 鼓励采用内存安全语言;
- 推动供应商给出更安全的软件开发路线;
- 在关键基础设施和高风险软件里优先处理这类问题。
Rust 之所以在这种背景下格外突出,不是因为它被唯一指定,而是因为它正好落在一个很少见的位置上:既能做底层系统开发,又能把很多常见的内存错误拦在编译阶段。
Rust 解决的主要问题
传统系统语言给程序员很大的自由,也把很多风险交给程序员自己处理。以 C++ 为例,手动管理对象生命周期、指针、引用、移动语义、线程共享数据,都可以写出非常高效的程序;但一旦处理不慎,也容易出现悬垂指针、重复释放、数据竞争等问题。
Python 则走了另一条路。它通过解释执行、自动内存管理和动态类型,让编程体验变得轻松很多。写脚本、处理数据、快速验证想法时,Python 非常顺手。但代价是性能通常不如编译型语言,很多错误也要等运行时才暴露。
Rust 想提供的是第三种选择:
- 性能接近 C/C++;
- 没有垃圾回收器;
- 内存安全尽量由编译器保证;
- 并发错误尽量在编译期发现;
- 通过 Cargo 提供统一的构建、依赖和测试体验。
这并不意味着 Rust 比 C++ 或 Python “全面更好”。更准确地说,Rust 把很多原本依赖程序员经验、团队规范和代码审查去兜底的约束,前移到了编译器检查阶段。
所有权是 Rust 的核心
Rust 最重要的概念是 ownership,也就是“所有权”。
在 Rust 中,每个值都有一个所有者。当所有者离开作用域时,这个值会被自动释放。通常情况下,一个值同一时间只能有一个所有者。
例如:
fn main() {
let s = String::from("hello");
let t = s;
println!("{}", t);
}这里 s 创建了一个堆上的字符串,随后所有权移动给了 t。移动之后,s 就不能再使用。
这和 C++ 的 move 有相似之处,但 Rust 把这种规则做成了语言层面的默认约束。编译器会强制检查移动后的变量是否被继续使用。
如果希望临时使用一个值,而不转移所有权,就使用借用:
fn len(s: &String) -> usize {
s.len()
}
fn main() {
let s = String::from("hello");
let n = len(&s);
println!("{} {}", s, n);
}这里 &s 表示不可变借用。函数可以读取 s,但不会拿走它的所有权。
Rust 的借用规则可以粗略记成两条:
- 可以有多个不可变引用;
- 或者有一个可变引用;
- 但不能同时既有可变引用,又有其他引用。
这套规则看起来严格,但它正是 Rust 能在没有垃圾回收的情况下保证内存安全和数据竞争安全的基础。
和 C++ 的区别
Rust 和 C++ 都适合写对性能要求高的程序,例如系统软件、编译器、数据库、网络服务、仿真程序和底层库,但两者的设计哲学差异很大。
内存管理
C++ 依靠构造函数、析构函数、RAII、智能指针和移动语义管理资源。这套机制非常强大,但也很复杂。程序员需要清楚对象何时创建、何时销毁、何时复制、何时移动,以及引用是否仍然有效。
Rust 也使用 RAII 思想,但所有权和借用规则更严格。很多在 C++ 中合法但危险的写法,在 Rust 中会直接无法通过编译。
例如,Rust 编译器会阻止悬垂引用:
fn dangling() -> &String {
let s = String::from("hello");
&s
}这段代码不能编译,因为 s 在函数结束时会被释放,返回它的引用就是悬垂引用。
在 C++ 中,类似错误也可能写出来,而且有时要到运行时才暴露。Rust 的优势在于把这类问题提前到编译期。
空指针和错误处理
C++ 中可以使用空指针,也可以通过异常处理错误。它们都很灵活,但大型工程里如果约定不统一,调用者很容易漏掉某些情况。
Rust 默认没有空引用。需要表达“可能没有值”时,使用 Option<T>:
fn find_name(id: u32) -> Option<String> {
if id == 1 {
Some(String::from("Alice"))
} else {
None
}
}需要表达“可能失败”时,使用 Result<T, E>:
fn divide(a: f64, b: f64) -> Result<f64, String> {
if b == 0.0 {
Err(String::from("division by zero"))
} else {
Ok(a / b)
}
}这样一来,调用者就必须显式处理成功和失败。错误路径不再只是注释或约定,而是类型系统的一部分。
模板与泛型
C++ 的模板极其强大,能做泛型编程、元编程和大量编译期计算。但模板错误信息往往复杂,学习成本也高。
Rust 也支持泛型,但通常配合 trait 使用。trait 可以理解为一组行为约束,类似接口:
fn print_value<T: std::fmt::Display>(x: T) {
println!("{}", x);
}这里 T: Display 表示传入的类型必须能被格式化输出。Rust 的泛型约束通常更显式,错误信息也更围绕 trait 展开。
生态和工程体验
C++ 的生态非常庞大,但构建系统比较分散:Makefile、CMake、Bazel、Meson、vcpkg、Conan 等都可能出现。不同项目之间的依赖管理体验差异很大。
Rust 默认使用 Cargo:
cargo new hello-rust
cargo build
cargo run
cargo testCargo 同时处理项目创建、构建、依赖、测试、文档和发布。对新项目来说,这种统一体验非常省心。
和 Python 的区别
Rust 和 Python 的差异更明显。Python 追求快速开发和表达力,Rust 更强调静态检查、性能和可控性。
类型系统
Python 是动态类型语言。变量不固定绑定某个类型,同一个函数可以接收各种对象,只要运行时支持相应操作即可。
def add(a, b):
return a + b这很灵活,也很适合写脚本。但如果类型不符合预期,错误往往运行到那一行才出现。
Rust 是静态类型语言。类型在编译期确定:
fn add(a: i32, b: i32) -> i32 {
a + b
}Rust 有类型推断,不需要每个地方都手写类型,但函数边界通常会写清楚。这让大型项目更容易依赖编译器来检查接口变化。
性能模型
Python 通常通过解释器运行,很多对象都在堆上管理,函数调用、动态分发和对象模型都有额外开销。它适合胶水代码、数据处理、自动化脚本和快速原型。
Rust 编译成本地机器码,没有全局解释器锁这类语言级限制,也没有默认的垃圾回收停顿。它适合写性能敏感、资源敏感或需要长期运行的程序。
当然,Python 也可以通过 NumPy、PyTorch、Cython、Numba 或 C/C++ 扩展获得高性能。但这种高性能通常来自底层库,而不是纯 Python 代码本身。
开发速度
Python 的上手速度通常更快。想法可以很快写成脚本,调试也直接。对于一次性任务、数据清洗、小工具和算法验证,Python 仍然非常合适。
Rust 的前期开发速度通常会慢一些。所有权、借用、生命周期、trait、错误处理都需要适应。编译器会拦下很多写法,初学时很容易感觉“编译器一直在反对我”。
但当项目变大以后,Rust 的优势会逐渐显现:类型、所有权和错误处理会让重构更有把握。很多潜在问题会在编译阶段被发现,而不是等到线上运行时才暴露出来。
三者的对比
| 维度 | Rust | C++ | Python |
|---|---|---|---|
| 主要定位 | 系统编程、可靠高性能软件 | 系统编程、高性能软件、底层库 | 脚本、数据处理、Web、自动化、原型 |
| 执行方式 | 编译成本地代码 | 编译成本地代码 | 通常由解释器执行 |
| 内存管理 | 所有权、借用、RAII | 手动管理、RAII、智能指针 | 垃圾回收和引用计数 |
| 类型系统 | 静态强类型,类型推断 | 静态强类型,模板强大 | 动态类型,可选类型标注 |
| 空值处理 | Option<T> | 指针可为空,需约定 | None |
| 错误处理 | Result<T, E>、panic! | 异常、错误码、返回值 | 异常 |
| 并发安全 | 编译期阻止很多数据竞争 | 依赖程序员和库约束 | 受解释器和运行时模型影响 |
| 学习曲线 | 中高,所有权是门槛 | 高,语言特性复杂 | 低,上手快 |
| 工程工具 | Cargo 统一管理 | 工具链选择多但分散 | pip、venv、poetry 等 |
总结
Rust 的价值不只是“快”,而是把性能、安全和工程可维护性放在同一个设计里统一考虑。
和 C++ 相比,Rust 更愿意用严格的编译期规则换取内存安全和并发安全;和 Python 相比,Rust 放弃了一部分动态语言的自由,换来了更强的类型检查和更稳定的性能模型。
所以,学习 Rust 的过程,本质上也是重新理解程序资源管理的过程:值属于谁,谁可以读,谁可以改,什么时候释放,错误应该如何表达。
版权所有
版权归属:Guisong Wu