Rust 语言,作为一门充满活力的计算机编程语言,近年来逐渐成为业界的焦点。在 Stack Overflow 的年度开发者调查报告中,Rust 连续多年被评为“最受喜爱的编程语言”,越来越多的大公司如谷歌、微软、腾讯等都开始将其运用于各类项目中。
尽管 Rust 语言颇受欢迎,我们发现很多学生和初级程序员对它的了解仍显不足。作为一个使用 Rust 语言开发的开源数据库项目团队,我们希望能帮助更多人深入了解这门安全、高效的编程语言。因此,本文将向大家介绍 Rust 的起源、优势,并为有意学习 Rust 语言的朋友们提供一些我们认为还不错的学习资源。
Rust 语言是什么
由来
Rust 最初由 Mozilla 的员工 Graydon Hoare 开发,Mozilla 于 2009 年开始赞助该项目,作为正在进行的名为 Servo 的实验性浏览器引擎开发的一部分,并于 2010 年首次亮相。
Rust 的开发团队在 Mozilla 的帮助下,不断改进语言的设计和实现,直到 2015 年 Rust 1.0 正式发布。Rust 的设计灵感来自于 C++、C#、Haskell 和 Erlang 等多种编程语言,具有其它语言所不具备的一些独特特性。作为一门现代系统编程语言,其旨在提供内存安全、高性能和可靠性。
Rust 语法
Rust 的语法类似于 C++,它采用了一些现代编程语言的特性,如模式匹配、闭包和迭代器等,同时也继承了 C++ 的语法和底层系统编程能力。而为了解决 C++ 存在的内存安全问题和并发性问题,Rust 引入了一个严格的所有权模型和类型系统,能够在编译期间检测内存错误和数据竞争问题,从而确保程序的安全性和稳定性。
Rust 的优势
高性能
Rust 的高性能是指其能够在保持内存安全和语言特性丰富的前提下,提供接近于 C++ 的高性能表现。Rust 的高性能主要得益于其对于内存分配、多线程处理和零成本抽象等方面的优化。
如果把 Rust、C++ 和 Golang 比作三个要参加跑步比赛的运动员:
C++ 无疑是一位经验丰富的选手,他有很高的速度和灵活性。然而,C++ 需要自己负责“调整呼吸和心率”等许多事项(内存管理),这使得他在比赛中容易出错,导致失速或摔倒(内存泄漏、悬垂指针等问题)。
Golang 运动员则是一位新锐选手,其优势在于有一个智能的教练(垃圾回收器)在比赛中为他调整呼吸和心率。虽然这样可以避免很多失误,但是需要不断与教练沟通,这会减慢他的速度。
而 Rust 运动员则是一位全能选手。他借鉴了 C++ 运动员的速度和灵活性,同时又学会了像 Golang 一样在比赛前就制定好详细的计划(所有权、借用和生命周期等机制)。这使得 Rust 运动员在比赛中既能保持高速度,又能避免失误。

Source: Discord 上图是 Discord 进行的负载测试,明显可以看到用 Golang 的情况下,每隔一段时间系统就会有一个负载峰值,这就是垃圾回收器在作怪,而改成 Rust 实现之后,整体就平滑很多。同时在 Rust 版本中,延迟、CPU 和内存都要好一些。
可靠性
想象一下 Rust 和其他编程语言(如 C++)是两家餐厅,它们各自的可靠性是如何体现在食物制作过程中的呢?
C++ 餐厅制作食物的过程相对自由。厨师可以随意调整食材、烹饪方法和加工方式。虽然这样的灵活性可能带来很多创新,但在这个过程中,食物容易受到污染,比如厨师在切生鱼片时使用了切生肉的刀具,可能导致食物中毒。这就像 C++ 在处理内存和资源时,它的灵活性可能导致诸如内存泄漏、空指针解引用和数据竞争等问题。
而 Rust 餐厅则更加严格和规范。在制作食物的过程中,Rust 餐厅会对食材的来源、厨师的操作、烹饪方法等进行严格的监管。比如,它会要求在切生鱼片之前,厨师必须先换一把干净的刀具。Rust 餐厅的严格监管确保了食物质量和安全性,减少了因为疏忽而导致的问题。
Rust 在编译阶段通过借用检查器(Borrow Checker)进行内存安全检查,确保在内存操作过程中遵循严格的规范。通过编译时的检查减少程序运行时的错误,从而提高了整体的代码质量和安全性。
内存安全
Rust 的内存安全优势在于,这些机制会在编译阶段就检查程序的内存管理是否正确,防止程序员在编写代码时犯下诸如内存泄漏、双重释放和悬垂指针等错误。
这就像餐厅的管理者预先规划了菜肴的烹饪和清理流程,确保餐厅始终保持卫生和高效。因此,相较于其他编程语言,Rust 更能确保内存安全和程序的稳定性。
我们尝试写点代码看看,下面是一个错误的示例:
fn main() {
let r;
{
let x = 5;
r = &x;
}
println!("r: {}", r);
}
在上述这段代码中,我们在外部作用域中定义了一个 r
。在内部作用域中,定义了 x
, 并把 x
的引用赋值给了 r
,最后我们在外部作用域中访问了 r
。
我们尝试编译下上述代码,Rust 编译器会给出详细的错误信息。如下所示:
error[E0597]: `x` does not live long enough
--> src/main.rs:6:13
|
5 | let x = 5;
| - binding `x` declared here
6 | r = &x;
| ^^ borrowed value does not live long enough
7 | }
| - `x` dropped here while still borrowed
8 |
9 | println!("r: {}", r);
| - borrow later used here
For more information about this error, try `rustc --explain E0597`.
error: could not compile `safe_memory` (bin "safe_memory") due to previous error
那为什么是错误的?上面的报错信息就已经很详细了,我们复述一下。因为变量 x
并没有 “存在地足够久”。当 x
到达第七行时,就离开了作用域,会被 drop 掉,此时外部作用域的 r
就变成了悬垂引用,所以在第 9 行访问 r
时,就会报错。
如果去掉第九行的话,编译就会通过。因为编译器知道在 x
被 drop 之后 ,没有地方可以再访问到悬垂引用 r
了。
小结一下:Rust 的一个重要特性是内存安全,Rust 编译器在编译期间会检查程序中的内存安全问题,并尽可能让这些问题在编译期暴露出来,从而避免运行时出现内存错误。
并发安全
Rust 的并发安全是指其能够在多线程环境下保证线程安全,避免出现数据竞争和死锁等问题。Rust 的并发安全主要依靠其所有权、借用机制及生命周期,当一个值被多个线程引用时,Rust 会阻止这些线程同时访问这个值的可变引用,从而避免了数据竞争问题。
下面这个 demo 演示了如何使用 Rust 中的原子类型来实现一个线程安全的累加器。
use std::sync::{
atomic::{AtomicI32, Ordering},
Arc,
};
fn main() {
let counter = Arc::new(AtomicI32::new(0));
let mut handles = vec![];
for _ in 0..10 {
let counter = counter.clone();
let handle = std::thread::spawn(move || {
for _ in 0..1000 {
counter.fetch_add(1, Ordering::Relaxed);
}
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
// the result should be 10*1000=10000
println!("Counter value: {}", counter.load(Ordering::Relaxed));
}
Rust 生态
Rust 语言虽然没有 JAVA、C++ 那么成熟,但近年来它周边的生态系统也在不断发展,已经拥有了很多库和框架。 一些 Rust 周边生态中的主要部分有:
包管理器:Cargo 是 Rust 的官方包管理器,用于构建、测试、发布 Rust 应用程序和库。
Web 开发:Actix 和 Rocket 是 Rust 中最受欢迎的 Web 框架,提供了构建高性能、安全的 Web 应用程序所需的功能。
异步编程:Tokio 和 async-std 是 Rust 中用于异步编程的两个主要库,提供了异步 I/O,定时器和任务调度等功能。
操作系统开发:Rust 在操作系统开发方面也具有潜力,Redox 是一个用 Rust 编写的微内核操作系统,代表了 Rust 在系统编程领域的实力。
这只是 Rust 生态系统中的一部分。随着 Rust 社区的不断发展,可以期待更多的库和框架会出现。
Rust 学习渠道
除了在 Rust 的官方 GitHub 入门之外,还可以通过以下书籍进行学习,这些书籍涵盖了从基础到高级的 Rust 知识,适合不同水平的读者阅读。
入门
The Rust Programming Language
Programming Rust
中级
Rust for Rustaceans
Rust Atomics and Locks
The Rust Reference
高级
- The Rustonomicon
当然也可以看 Rust 专家的视频讲解,比如:
Jon Gjengset(youtube.com/c/jongjengset),这是一位讲解 Rust 语言的博主,同时他也是麻省理工学院的教授,他的视频内容会更加进阶,适合已经有一定 Rust 语言基础的受众。
入门之后,还可以通过一些练手项目更深入地学习和使用 Rust,比如:
Tokio,一个异步的运行时用于编写高性能的网络应用程序
Serde,一个用于序列化和反序列化 Rust 数据结构的库
Diesel,一个 Rust 的 ORM(对象关系映射)库,用于将 Rust 数据结构映射到关系型数据库中
这些项目都可以帮你在 Rust 的使用方面更加精进。
当然,你也可以选择我们的 GreptimeDB 项目进行练习,GreptimeDB 是用 Rust 语言编写的开源时序数据库项目。我们提供了一系列适合新手的 good first issues,欢迎前往 GitHub 查看我们的项目。
总结
在 1.0 发布前,Rust 断舍离了内部的 green threads 实现,彻底坚定了这门语言作为一个无运行时、低抽象层级的设定。即便是之后标准库引入了异步 Future API,这一特色仍然保持。
低抽象级别带来的更细更强的控制力,使得 Rust 开发者可以更精准地控制资源(线程、内存),非常适合诸如服务器、数据库等场景的开发,直接对标 C++ 的传统领域。但控制力的背面是高风险,C++ 因其风险性广受诟病,也促使 Mozilla 选择尝试新语言。与之前 20 年中诸如 Java 、Go 等通过牺牲控制力降低风险的解决方案不同,Rust 选择用编写和编译时的强限制,比如让人又爱又恨的 borrow checker 和 lifetime ,保全了运行时的高性能和安全性。
当然,代价是相对陡峭的学习曲线和较长的编译时间。在这方面,Rust 在早期就成立了工具团队,持续在用更好的开发工具来降低其负面影响。
关于 Greptime
Greptime 格睿科技专注于为可观测、物联网及车联网等领域提供实时、高效的数据存储和分析服务,帮助客户挖掘数据的深层价值。目前基于云原生的时序数据库 GreptimeDB 已经衍生出多款适合不同用户的解决方案,更多信息或 demo 展示请联系下方小助手(微信号:greptime)。
欢迎对开源感兴趣的朋友们参与贡献和讨论,从带有 good first issue 标签的 issue 开始你的开源之旅吧~期待在开源社群里遇见你!添加小助手微信即可加入“技术交流群”与志同道合的朋友们面对面交流哦~
Star us on GitHub Now: https://github.com/GreptimeTeam/greptimedb
Twitter: https://twitter.com/Greptime
Slack: https://greptime.com/slack