Rust 洞见

降低 Rust 的 Learning Curve

发布时间:2025-05-05

我看到人们在学习 Rust 时一次又一次地犯同样的错误。以下是我关于如何简化学习过程的一些想法(按重要性排序)。我的目标是帮助你节省时间和避免挫败感。

放下你的戒备

停止抵抗。这是最重要的一课。

接受学习 Rust 需要采用与你习惯的完全不同的思维模式。有很多新的概念需要学习,比如 lifetimes、ownership 和 trait 系统。根据你的背景,你还需要将 generics、pattern matching 或 macros 添加到列表中。

你的学习速度与你是否聪明或是否有丰富的编程经验关系不大。相反,更重要的是你对这门语言的态度

我见过没有事先培训的初级开发人员在 Rust 方面表现出色,也见过高级工程师苦苦挣扎数周/数月甚至完全放弃。把你的傲慢留在家里。

将 borrow checker 视为合作者,而不是对手。这重新定义了这种关系。让编译器来教学:例如,这在 lifetimes 方面效果很好,因为编译器会告诉你 lifetime 何时不明确。然后只需添加它,但花时间思考_为什么_编译器无法自行解决。

fn longest(x: &str, y: &str) -> &str {
  if x.len() > y.len() {
    x
} else {
    y
}
}

如果你尝试编译这段代码,编译器会要求你添加一个 lifetime 参数。它提供了这个有用的建议:

1 | fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
 |      ++++   ++     ++     ++

因此,你无需猜测编译器想要什么,可以按照它的指示进行操作。但也要坐下来想想_为什么_编译器无法自行解决。

大多数时候,与编译器作斗争实际上是在暴露一个设计缺陷。类似地,如果你的代码变得过于冗长或看起来很丑陋,则可能有一种更好的方法。承认失败,并学习以 Rust 的方式来做。

如果你来自像 Python 这样的动态语言,你会发现 Rust 通常更冗长。不过,其中大部分只是来自 type annotations。有些人可能会认为 Rust “不优雅”或“丑陋”,但冗长实际上服务于一个很好的目的,并且对构建大规模应用程序非常有帮助:

从第一天起就启用所有 clippy lints —— 即使是那些迂腐的 lints。运行 linter 并严格按照建议进行操作。不要在程序编译后跳过此步骤。

抵抗是徒劳的。你拒绝学习的时间越长,你受到的痛苦就越多;但是你放下戒备的那一刻,就是你开始学习的那一刻。忘记你认为你对编程的了解,真正开始倾听编译器、标准库和 clippy 试图告诉你的内容。

迈小步

我当然试图在学会走路之前跑步。仅此一点就浪费了我很多宝贵的时间。

一开始不要对自己要求太高。以下是一些提示:

不要同时引入太多的新概念!相反,当你学习一个新概念时,打开一个编辑器并写出几个例子。有帮助的是,在 Rust playground 中编写一些代码,并尝试使其编译。编写非常小的代码片段(例如,一个 main.rs 对应一个概念),而不是使用一个大的“教程” repo。养成扔掉大部分代码的习惯。

我现在仍然这样做,并在 playground 中测试想法或与客户集思广益时。

例如,这是我最喜欢的代码片段之一,用于解释 ownership 的概念:

fn my_func(v: String){
  // do something with v
}
fn main(){
  let s = String::from("hello");
  my_func(s);
  my_func(s); // error, but why?
}

你能修复它吗?你能解释它吗?问问自己,如果 v 是一个 i32,会发生什么变化。

如果 Rust 代码对你来说很可怕,把它分解。编写你自己的、更简单的版本,然后慢慢增加复杂性。Rust 比阅读更容易编写。通过编写大量的 Rust,你也将学会更好地阅读它。

准确

你如何做任何事情,就是你如何做所有事情。—— 一句古老的 Rust 谚语

你可以在其他语言中马虎,但在 Rust 中不行。这意味着你必须在编写代码时保持准确,否则代码将无法编译。期望这种方法可以在将来节省你的调试时间。

我发现,学习 Rust 最快的人都非常注重细节。如果你试图只是完成任务并继续前进,那么如果你旨在第一次就做对,你将会遇到更大的困难。如果在按“编译”之前重新阅读你的代码以修复愚蠢的拼写错误,你将会度过一段更美好的时光。还要养成在必要时自动添加 &mut 的习惯。

一个在编码时会考虑这些细节的好例子是 Tsoding。例如,观看 这个直播,他从头开始用 Rust 构建了一个搜索引擎,看看我的意思。我认为只要你尽最大努力并给它一些时间,你就可以学到这项技能。

不要作弊

有了今天的工具,很容易将大部分工作分担给计算机。最初,你会感觉自己进展很快,但实际上,你只是加强了工作流程中的坏习惯。如果你无法向其他人解释你写的东西,或者你不知道代码的某个部分所做的权衡/假设,那么你就太过分了。

通常,这种方法源于担心你没有足够快地取得进展。但是你无需向别人证明你足够聪明,可以很快地掌握 Rust。

脚踏实地

要正确学习 Rust,你实际上必须手工编写大量的代码。不要成为 Reddit 上的潜水者,阅读其他人的成功故事。参与其中!投入时间,因为没有灵丹妙药。一旦它起作用,即使你知道它并不完美,也要考虑开源你的代码。

不要进入自动驾驶模式

LLMs 就像在自动驾驶模式下驾驶汽车。起初很舒服,但你不会感到掌控,而且慢慢地,那种不安的感觉会蔓延开来。学习时关闭自动驾驶仪。

一个快速让你取得成功的方法是先在 Rust Playground 中编写代码来学习。不要使用 LLMs 或代码补全。只需输入即可!如果你不能,这意味着你还没有完全内化一个概念。没关系!转到标准库并阅读文档。无论花费多长时间,然后回来再试一次。

慢就是稳,稳就是快。

培养肌肉记忆

编程中的肌肉记忆被严重低估了。人们会告诉你这是代码补全的目的,但我认为这是达到流畅状态的必要条件:如果你不断地在语法错误上犯错,或者更糟的是,只是等待下一次自动完成来取得进展,那将是一种糟糕的开发者体验。

手动编写时,你会犯更多的错误。拥抱他们!这些错误将帮助你了解编译器输出。你将对不同错误场景下的输出外观产生“感觉”。不要忽略这些错误。随着时间的推移,你将对什么感觉“rustic”产生一种直觉。

预测输出

我喜欢做的另一件事是运行“预测练习”,在运行代码之前猜测代码是否会编译。这会建立直觉。尝试使每个程序在运行之前都没有语法错误。不要马虎。当然,你不会总是成功,但随着时间的推移,你会做得更好。

尝试自己解决问题,_然后_再查找解决方案。

阅读大量其他人的代码。我推荐 ripgrep,例如,这是最好的 Rust 代码之一。

培养健康的阅读/编写代码的比例。

不要害怕弄脏你的手。你避免 Rust 的哪些领域?你逃避什么?专注于此。解决你的盲点。跟踪你常用的“逃生舱口”(unsafe, clone, 等等)以识别你当前的弱点。例如,如果你害怕 proc macros,请编写一堆。

破坏你的代码

完成练习后,破坏它!看看编译器说了什么。看看你能不能解释发生了什么。

学习时不要使用其他人的 Crates

一个糟糕的个人版本胜过一个完美的外部 crate(至少在学习时)。自己编写一些小的库代码作为练习。值得注意的例外可能是 serdeanyhow,它们可以节省你处理 JSON 输入和设置错误处理的时间,你可以将这些时间用于其他任务,只要你知道它们是如何工作的。

建立良好的直觉

像 lifetimes 这样的概念很难掌握。有时,绘制数据如何在你的系统中移动会有所帮助。养成通过绘图向自己和他人解释概念的习惯。我不确定,但我认为这对“视觉”/创意人士(与高度分析人士相比)最有效。

我个人使用 excalidraw 进行绘图。它具有一种“comicy”的感觉,这减轻了一点压力。这意味着它感觉不太准确,而更像是一个粗略的草图。许多优秀的工程师(以及伟大的数学家和物理学家)都能够通过草图可视化概念。

在 Rust 中,草图可以帮助可视化数据或架构图的 lifetimes 和 ownership。

在你已经知道的基础上构建

早些时候我说过你应该忘记你对编程的所有了解。我现在怎么能声称你应该在你已经知道的基础上构建呢?

我的意思是说,Rust 在控制流处理和值传递等熟悉的领域中最为不同。例如,mutability 在 Rust 中非常明确,并且调用函数通常会“移动”其参数。这就是你必须接受 Rust 只是_不同_并从_第一性原理_学习的地方。

但是,可以将 Rust 概念映射到你已经知道的其他语言是可以的。例如,“trait 有点像 interface”是错误的,但这是一个理解概念的良好起点。

以下是一些更多示例:

如果你有函数式编程的背景,它可能是:

这个想法是,映射概念可以帮助更快地填补空白。

将你已经从另一种语言(例如 Python, TypeScript)中了解到的知识映射到 Rust 概念。只要你知道存在细微的差异,我认为它是有帮助的。

我没有看到很多人提到这一点,但我认为 Rosetta Code 是一个很好的资源。你基本上浏览他们的任务列表,选择一个你喜欢的,然后开始将 Rust 解决方案与你最擅长的语言进行比较。

此外,将代码从你了解的语言移植到 Rust。这样,你不必在学习 Rust 的同时学习一个新的领域。你可以在你现有的知识和经验的基础上构建。

最后,找到与你来自相同背景的其他人。阅读他们的博客,他们会在那里谈论他们学习 Rust 的经历。也写下你的经历。

不要猜测

我发现那些倾向于通过_猜测_来应对挑战的人通常很难学习 Rust。

在 Rust 中,细节决定一切。不要忽略细节,因为它们总是会揭示一些关于手头任务的智慧。即使你不在乎细节,它们也会在以后反咬你一口。

例如,为什么你必须在已经是一个字符串的东西上调用 to_string()

my_func("hello".to_string())

这些绊脚石是学习的机会。提出这些问题可能看起来是在浪费时间,意味着完成任务需要更长的时间,但从长远来看,它会得到回报。

认真阅读编译器打印的错误消息。每个人都认为他们会这样做,但我一次又一次地看到人们看起来很困惑,而解决方案就在他们的终端中。还有 hints;不要忽略这些。仅此一项就可以节省你很多时间。稍后感谢我。

你可能会说这对每种语言都是如此,你是对的。但是在 Rust 中,错误消息实际上值得你花费时间。其中一些就像小冥想:有机会从更深层次思考问题。

如果你收到任何 borrow-checker 错误,请拒绝猜测发生了什么的冲动。不要猜测,手工遍历数据流(谁拥有什么以及何时)。尝试自己想清楚,只有在了解问题后才尝试再次编译。

依靠类型驱动开发

好的 Rust 代码的关键在于它的类型系统。

一切都在类型系统中。你需要的一切都隐藏在众目睽睽之下。但通常,人们会跳过太多的文档,而只看示例。

很少有人_阅读实际的函数文档_。你甚至可以点击标准库,一直到源代码,以阅读他们正在使用的东西。没有魔法(这就是它如此神奇的原因)。

你可以在 Rust 中比在大多数其他语言中做得更好。那是因为例如 Python 是用 C 编写的,这需要你跨越该语言边界才能了解正在发生的事情。类似地,C++ 标准库不是一个单一的、标准化的实现,而是有几个不同的实现由不同的组织维护。这使得很难知道_到底发生了什么_。在 Rust 中,源代码就在文档中。好好利用这一点!

函数签名说明了很多!你越早接受这些额外的信息,你就会越快地开始使用 Rust。如果你有时间,请阅读标准库文档中有趣的部分。即使几年后,当我这样做时,我总是能学到一些东西。

首先尝试使用类型来建模你自己的项目。这是你开始对这门语言产生更多乐趣的时候。感觉就像你正在与编译器就你试图解决的问题进行对话。

例如,一旦你了解了像表达式、迭代器和 trait 这样的概念如何结合在一起,你就可以编写更简洁、更易读的代码。

一旦你学会了如何在类型中编码不变量,你就可以编写更多正确的代码,而无需运行来测试。相反,你首先无法编译不正确的代码。

通过“类型驱动开发”学习 Rust,让编译器错误指导你的设计。

花时间寻找好的学习资源

在开始之前,货比三家,找到适合你个人学习风格的资源。老实说,现在还没有那么多好的东西。从好的方面来说,在确定一个特定的平台/书籍/课程_之前_,花太长时间来浏览资源列表。正确的资源取决于你是哪种学习者。从长远来看,找到正确的资源可以节省你的时间,因为你将学习得更快。

我个人不喜欢做别人为我构建的玩具练习。这就是为什么我不太喜欢 Rustlings;这些练习“不好玩”而且太理论化了。我想要更实用的练习。我发现 Project EulerAdvent of Code 对我来说效果更好。这个问题经常出现,所以我写了一篇关于 我最喜欢的 Rust 学习资源 的博文。

不要只看 YouTube

我喜欢看 YouTube,但仅用于娱乐目的。在我看来,观看 ThePrimeagen 仅用于娱乐。他是一位了不起的程序员,但试图通过观看别人来学习如何编程就像试图通过观看奥运会来学习如何成为一名伟大的运动员。类似地,我认为我们都可以同意 Jon Gjengset 是一位杰出的程序员和老师,但如果你刚开始,观看他可能会让人不知所措。(喜欢这些内容!)

会议演讲或播客也是如此:它们对于上下文和软技能很有用,但不适合学习 Rust。

相反,如果可以的话,投资一本好书。书籍还没有过时,你可以离线阅读,添加个人笔记,自己输入代码,并通过翻阅页面获得对内容深度的“空间概述”。

类似地,如果你认真地想在职业上使用 Rust,请购买课程或让你的老板投资培训师。当然,我在这里非常有偏见,因为我经营一家 Rust 咨询公司,但我真的相信它会为你和你的公司节省无数的时间,并将你设置为长期成功。考虑一下:你将在未来几年内使用这个代码库。最好让这种体验愉快。一位好的培训师,就像一位好的老师一样,不会与你一起学习 Rust 书,而是会观察你在野外编写 Rust 代码,并为你提供关于你的弱点的个性化反馈。

找到一个编码伙伴

“Shadow”更有经验的团队成员或朋友。

不要害怕在 Mastodon 或 Rust 论坛 上寻求代码审查,并回报这种恩惠并在那里自己进行代码审查。抓住结对编程的机会。

向非 Rust 开发者解释 Rust 代码

这是查看你是否真正理解某个概念的绝佳方式。不要害怕说“我不知道。”然后直接查看文档,一起探索答案。这更有意义,也更诚实。

帮助修复被遗弃的 OSS 代码。如果你为修复未维护的代码库付出了扎实的努力,你将在学习如何使用其他人的 Rust 代码的同时帮助其他人。

大声朗读代码并解释它。这样做并不丢人!它可以帮助你“序列化”你的想法并避免跳过重要的细节。

做笔记。编写你自己的小“Rust 词汇表”,将 Rust 术语映射到你业务领域中的概念。它不必是完整的,只需满足你的需求即可。

写下你觉得困难的事情和你学到的东西。如果你找到一个很棒的学习资源,请分享它!

相信长期的好处

如果你学习 Rust 是因为你想把它写在你的简历上,那就停止吧。学习其他的东西代替。

我认为你必须真正_喜欢_编程(而不仅仅是它的想法)才能享受 Rust。

如果你想在 Rust 方面取得成功,你必须长期参与其中。设定切合实际的期望:你不会在一周内成为“Rust 大师”,但你可以在一个月的集中努力中取得很多成就。没有灵丹妙药,但如果你避免了最常见的搬起石头砸自己的脚的方法,你就可以更快地掌握这门语言。Rust 是一门 day 2 语言。你不会像在 Go 或 Python 的第一周那样“感觉”高效,但坚持下去,它会得到回报的。祝你好运,玩得开心!