特别策划 学习锦囊(二):听听课代表们怎么说 你好,我是Rust课程的编辑叶芊。今天是特邀课代表分享个人学习经验的第二辑。

一直有新同学困惑到底怎么学才能更丝滑地上手:为什么我看了前6讲越看越懵?到底需要什么知识背景才能学Rust,才能学这个专栏?希望能提供专栏的食用手册,或者做相关背景知识整理之类的需求……

为了帮助你在新的一年更好地学习,我特地邀请了几位课代表来分享一下他们学习专栏、学习Rust语言的个人经验和方法,希望能给你一些参考和启发。学习愉快。

@Marvichov

你好,我是Marvichov,是一名工龄5年的软件工程师。目前的工作领域是分布式机器学习,主力语言是Python。之前也有Cpp的工作经验,搞过两年半搜索引擎,也做过一年半开源项目。平时也使用Java和Go进行个人项目开发和学习。

和大多数人一样,立了flag要好好学完这门课程,结果止步于aysnc。

学习一门语言,先要明白为什么要学。世界上的语言千千万万,学习英语不也挺香?对我来说学Rust的理由很简单,Rust的安全模型很有意思,确实能解决很多C/CPP的痛点。对于底层开发来说,安全越来越重要,有着比性能更高的优先级。

分享个同事的例子:自从项目从C转到Rust之后,他晚上能安心睡觉了。不会因为突如其来的Segfault半夜被叫醒。某些多线程bug,debug一个月是常态。现在,他可以安心merge新人的PR了,因为有compiler去阻断有安全隐患的代码。

不出意外的话,未来Rust会制霸底层。如果要搞底层开发的话,还是绕不开这座大山

最开始学的时候,我先去读了官方the book,算是对Rust有了一些初步、直观认识。然而,The book写得比较浅,各种知识点都是点到为止。尤其是最重要的lifetime和borrow check,the book没有讲得很深入,我看完还是不知道compiler是怎么计算lifetime的。更深入学习的话,还是要求助于死灵书和官方reference

后来遇到了Non-Lexical Lifetime (NLL),深入到了compiler的实现细节,我愈加发现自己力不从心,钻语法的牛角尖。花了大量的时间钻研语言本身,而非积累实战项目经验。

这和我之前学习CPP的经历很相似:纠结各种语法特性,浪费了很多时间在死记硬背实战用不到的语法上。到头来,CPP还是靠不断在工作中做项目熟练掌握的。况且,大部分语法,实战中根本不会用。花了大量时间纸上谈兵。到头来不是在学习,而是在感动自己。

因此,学习一门语言,我还是倾向于快速上手,直接撸项目。很多大神学习新语言的方法就是用新语言把自己熟悉的项目重新写一遍

我记得耗子叔就在《听风》专栏讲过,他学语言就是看这个几个主要方面:内存管理、错误处理、类型系统等等,然后花一两天写个小项目就把一门语言掌握了。各个语言都是大同小异的,学习多了之后就能很快触类旁通。然而,我并没有什么端到端的项目经历,这种方法对我来说还是挺有难度的。

不过,对于大多数人包括我来说,熟悉语言还有一种快速上手的方法,就是刷题,把那些之前写过的算法,用Rust写一遍。

Leetcode对Rust的支持很一般,发生错误之后也没有stack trace。我个人就选择用exercism,所有代码和测试都可以在本地跑,方便调试。我周围很多Rust工程师就是通过刷题快速上手Rust,然后被委任去重写一些Java项目。这个方法你也可以参考。

正好陈天老师的Rust专栏提供了很多动手的实战小项目,弥补了市面上各种Rust书籍的缺点,也避免了最后停留在学习语言本身的误区上。毕竟,语言是用来解决实际问题的。我们学习Rust也不是为了掉书袋,而是为了让程序创造价值。

其实,我当时很犹豫要不要买课,因为语法知识点在官方的the book和死灵书上都讲得很详细了。但是一看到第六讲的实战项目,就毫不犹豫下单了,我自己对compiler就很感兴趣,能手撸一个SQL解析器,还是用Rust,一石二鸟岂不美哉。

我是怎么学专栏的

在这里也分享一下我自己是怎么学习这门课的,希望能给你参考。

  • 1-3讲 内存前置知识

如果想搞清楚内存是如何被管理的,或者想深入理解程序的address space,推荐上一上《Computer Systems: A Programmer’s Perspective》(CSAPP)。这门课的教授Dave说:“如果你的一生只上一门计算机系统基础,CSAPP就够了”。我后来补了一部分这个课,的的确确帮助我理解了许多系统底层原理。

简单来说,在机器码或者汇编层面,没有ownership一说,也没有lifetime,有的只是数据和一连串的指令。CPU只知道执行指令、数据传输、读写各种寄存器,以及内存,例如程序员视角的stack和heap。

Owership和lifetime只是high level语言层面的抽象,属于Rust语言的一部分,并不是计算机最后执行的机器码的一部分。就像算法里面的loop invariant一样,通过在高级语言语法层面的规则限制,保证最后编译出的代码不会在runtime出现导致内存安全的错误。

  • 4-6讲 get hands dirty

我第一遍学的时候就快速过了一遍,不求完全理解语法细节。大概知道用Rust写项目很灵活、Rust支持很多domain就行了。这几节课的信息量有点大,很容易劝退新手,暂时搞不懂的就放在那里,等以后学到了,再回来过一遍。

  • 7-14讲 Ownership & Containers

基础中的基础。首先了解ownership和lifetime,这是Rust相比于C要解决的核心问题:内存安全。这些课程的例子,我都是自己亲手一行一行打的。先把例子过一遍,然后自己写一遍。毕竟根据学习金字塔原理,动手实践的学习效果,比单纯只是阅读要强50%。之后,弄明白smart pointer和各种基础数据结构,才能更快上手项目,或者刷题。

  • 18讲 错误处理

重点中的重点,也是Rust吸收其他语言优点的例子。built-in的语法支持,让Rust区别于其他主流语言,例如C/CPP、Java。错误处理的方式也可以窥见Rust的安全设计思想。

  • 12-14、23-25 Traits

Rust的核心之一就是Traits,是Rust语言抽象的地基。熟悉面向接口编程的抽象风格,才能跟上课程里的各种项目。很多Traits必须熟练掌握,比如AsRef、From、Deref、Drop、Send/Sync等等。这些Traits就像构建Rust世界的基础元素。如果不熟悉,就很难读文档、设计接口。

  • 21-22、26、36-37、41-42 KV server实操系列

这个系列第一遍学可以暂时放一下,等Rust知识点集齐了,再拉通一起学习更好。课程的安排是穿插学习KV server,中途不断补充新的知识点,然后不断用新的语法糖迭代这个项目。但是这个项目的代码量其实并不小,中途很容易忘记项目里面各种细节和上下文。我自己学的时候觉得拉通学习这个项目,趁热打铁,效果更好。

  • 其他讲

剩下的基本上都是项目实战,主要是熟悉各种IO和接口、系统设计。除了跟着老师的思路一步一步敲代码,我想不出更好的办法。老师的项目实战经验很丰富,很多设计我都需要推敲很久,才能理解。越学到后面,跟上老师就越费劲,因为不仅要学习老师的设计思想,同时还要学习Rust的各种知识点。只能说一门课当两门课上,非常实惠。

我的学习方法

第一个方法是画思维导图,一图胜千言。用自己的语言描述学过的知识点才能内化。老师在这方面真的是很好的榜样,每节课开头就是一张知识地图。

第二个方法就是构建自己的知识体系,核心思想是holisitic learning。你学的每个知识点都是一座岛屿,将它们能相互连接,你才能触类旁通,学过的知识就很难被忘记。

打个比方,如果各个知识点没有连接,就变成了孤岛,容易被人遗忘。当知识连接多了,孤岛就变成了城市。当你在城市迷路,很容易通过到新的导航达到目的地。但如果你在荒郊野岭迷路了,就需要付出相当大的代价才能达到目的地。很多时候,这样的代价是重头开始学。

我学习Rust的时候,就喜欢和C、CPP、Golang对比,把相似的知识点串联起来。

举个例子,CPP里面的template、RAII就和Rust里面的generics、Drop相对应;Golang里面的interface就和Rust的trait相似,都是interface oriented programming (面向接口编程)。Rust唯一和其他语言不同的就是安全模型。因此,我们在学习的时候,可以重点掌握owership、lifetime和thread safety。

第三点就是勤动手。私以为,写代码99%都是熟练工,没有捷径,也不需要天赋,有高中的数理逻辑就可以。行业内,只有1%的人能做mathematical programming,也就是创造、研究、优化和编写核心算法。成为这1%的人,才需要谈天赋。

Rust之所以难,不是因为编码者天赋不够,而是因为它要求编码者有良好的底层基础、和对内存模型有很好的认识。学Rust遇到瓶颈学不下去了,不妨退一步,补一补基础。这里再推一下CSAPP。

第四点前面提过了,除了跟着老师撸项目,还可以刷题。我在对刷题答案的时候,注意到很多同学喜欢通过函数式编程把复杂的逻辑揉成一行。这就像当年那些Python一行流,炫耀自己能一行刷一道题。Python这么写可以,因为没人会苛求Python代码有很好的性能。Rust这么写就不太合适了。

Rust语言,虽然其可表达性很强,同时也鼓励大家使用函数式,但是它还是一门底层语言。底层语言最重要的特性就是可读性、可优化性。当你把很复杂的程序,压缩成一行或者一个statement时候,你很难看出哪里需要优化。周围一些Rust工程师也告诉我,实战中没人会那么写代码。大部分时候,他们采用的是最简单直接的API、一目了然的Generics、以及面向接口编程的设计理念。

最后一点就是复盘

往小了说,就是及时复习。老调重弹一下中学学过的艾宾浩斯遗忘曲线。根据这个粗糙模型,一天不复习,就会忘70%。隔一周,忘77%。现在社会人比较忙,一周内抽空复习一下就好。不然学了、忘了,最后缓解了焦虑、感动了自己。

这里推荐一下大神Jon Gjengset分享的方法:上课的时候,他不会去记笔记,而是会全神贯注听,力求课上搞懂;课下的时候,他会使用康奈尔笔记法(Cornell Notes system)做笔记。这种笔记法专门为复习、抗遗忘而设计的。亲测有效。很多笔记软件都内置康奈尔笔记法模板。

往大了说,就是建立知识体系和方法论。课后,总结学到的知识点,通过前面提到的holistic learning的思路,将刚学的与之前学过的知识建立联系,顺便也复习之前的知识。

除了复盘知识点,也可以复盘自己的学习方法是否有效,学习计划安排得是否合理。复盘是一个非常自律的过程,我也在不断摸索。希望2022年和大家一起进步,不断成长。

学习资料

这是今天课代表Marvichov同学的分享,如果你有自己的Rust学习故事,欢迎在下方留言区留言互相交流,互相映证,共同学习,共同进步。

第三辑见~

参考资料

https://learn.lianglianglee.com/%e4%b8%93%e6%a0%8f/%e9%99%88%e5%a4%a9%20%c2%b7%20Rust%20%e7%bc%96%e7%a8%8b%e7%ac%ac%e4%b8%80%e8%af%be/%e7%89%b9%e5%88%ab%e7%ad%96%e5%88%92%20%e5%ad%a6%e4%b9%a0%e9%94%a6%e5%9b%8a%ef%bc%88%e4%ba%8c%ef%bc%89%ef%bc%9a%e5%90%ac%e5%90%ac%e8%af%be%e4%bb%a3%e8%a1%a8%e4%bb%ac%e6%80%8e%e4%b9%88%e8%af%b4.md