理清头脑混沌,觉醒心智天地python
在 2020 年 3 月 28 号,我为
上海科技大学GeekPie社团 WorkShop#7「关于Rust你须要了解的…」 共享了一次分享,本文是该分享的文字版。内容比视频里的多了一些细节描述,但尽量地精炼。
视频版见B站回放:https://www.bilibili.com/video/BV1ti4y1b7xy/
https://c-t.work/s/74b9dcf657be4d程序员
https://cowtransfer.com/s/c98f417a076d48 密码shanghaitech算法
https://c-t.work/s/37a60fd0da9041 密码shanghaitech 数据库
另附加两篇关于「K-Rust :Rust 可执行形式语义」的论文:编程
上科大宋老师: https://arxiv.org/abs/1804.10806 设计模式
Cyber Security Lab - NTU : https://arxiv.org/abs/1804.07608数组
今天给你们带来的主题是:编程语言中的变革者 | 勇于打造理想世界的 Rust 。
Rust 语言的诞生。任何一门技术的存在,它都是为了解决一个问题。那么 Rust 是为了解决什么问题而存在?这是咱们面对Rust语言的时候,要必需要先搞清楚的一个问题。缓存
Rust 语言有什么特性。经过了解Rust语言的特性,来看看 Rust 语言如何解决它想解决的那些问题。
了解 Rust 在生产环境中有哪些应用。
-
对于当今大学生来讲,学习 Rust 的意义何在?Rust 是如何让你成为一个更好的开发者?
Rust 语言到底想解决什么问题呢?这就要回顾一下计算机和编程语言发展历史了。
咱们如今思考一个问题,假如没有 Rust 语言,时代的进程是什么样的?
在历史的早期,程序员们写代码,都是直接机器码编程,就是纸带机,你们都在那戳一个个小孔来编程。
随着计算机发展,直接打孔编程的方式已经知足不了需求了,效率太慢,因而有人发明了汇编语言。
汇编语言是对机器码的一一对应,能够直接翻译为机器码,汇编语言第一次提高了代码的可读性。这个重要性,不亚于生物进化史上,猿猴学会了直立行走。
随着时代的发展,又有人发明了 C 语言来替代汇编语言。但其实,真正的历史不是这么一蹴而就的,从汇编到C语言,是伴随着Unix系统的发展,经历了汇编、A语言、B语言才最终达成了C语言。C语言拥有强大的功能,高性能,且不依赖于具体机器系统的可移植性,帮助Unix顺利发展。
Unix 和C 语言就这么相辅相成的,发展至今,统治了世界。你们确定都用过从Unix系统演化而来的Linux、BSD、MacOS等等。
后来,Bjarne博士在对Unix内核作分析的时候,发现没有合适的工具能有效分析内核分布而形成网络流量,以及怎样将内核模块化,因而他就考虑,是否应该发明一种新的语言。在当时若是想和C竞争,就必须错开和C的应用领域,而且同时拥有和C同样的性能。最后他基于C语言,而且从Simula汲取了类的概念,从Ada语言中取来了模板、名字空间和异常等,最终发明了Cpp语言,也就是 C with Class。
这在当时,Cpp的设计确实是很是先进的,而且在1995年以前在工业界很是流行。
可是当Java和C#出现以后,以及硬件价格开始大规模降低的时候,Cpp受到了必定的冲击。
其实在Java诞生前夜,Sun公司的人还在考虑使用C++来发明一种语言,可是他们发现,C++存在很大的问题,C++太复杂,以致于不少开发者在错误使用它。而且C++缺少可移植安全性、分布式程序设计和多线程功能、垃圾回收等。由于他们想要一种易于移植到各类设备平台的语言。
一直到1994年,Sun公司的人改变了努力的目标,他们认为,随着网景公司Mosaic浏览器的到来,互联网将变得愈来愈流行,而这一远景是他们在有线电视网中看到的。因而Java诞生了。Java随着浏览器的发展而发展,可是后来受到微软的阻拦以后,IE里虽然没有了Java平台,可是Java此时已经普及到了服务端和手持设备上。JSP和其余Java技术也就流行了起来。
随着互联网的高速发展,一直到2004年,Ruby on Rails的出世,带动了硅谷互联网创业浪潮,这算是互联网的加速时代。
一直到2015年,互联网创业浪潮才退去。互联网流量到了史无前例的高度。为了提供更稳定和更好的服务,互联网逐渐进入了云原生时代。Docker的横空出世,让Go语言也开始普及了。
咱们回顾了编程语言的发展历史,看的出来,基本上一些主流语言的诞生,都是随着时代的变化而出现。
好像没有Rust语言,这个时代也会继续发展下去,毫无影响。但其实咱们要再仔细的推敲一下,就不可贵出一些结论:
其实当时还有更加安全的语言Ada,其实Ada的设计理念很是先进,包括了「编译期类型检查、肯定性内存管理、内置安全并发模型,无数据竞争、泛型」等特性。可是为何没有成为主流呢?这是当时那个性能为王的时代的必然结果。Unix的发展,性能是第一,可是安全并非主要的,由于我的电脑并无普及开。
因此,追求性能,牺牲了安全性,因而造就了一个不安全的世界。
第2、Java为了突破C/Cpp的桎梏,进一步引入了GC
Java诞生之时,正是互联网发展初期,我的电脑获得了必定程度的普及,安全性开始被注重了起来。可是Java是经过引入GC来解决内存安全的问题,把内存管理交给程序和算法,这个思路是对的,可是势必会牺牲一部分性能。
第3、摩尔定律致使硬件降低,互联网创业潮,各类动态语言流行,开发效率倍增
高速的发展,势必会积累不少技术债务,软件的工程性遭到了挑战。编程语言上手门槛变得愈来愈低,这个时期,只注重生产力,码农和码畜由此诞生。
创业浪潮虽然退去,可是流量激增,操做系统的瓶颈凸显。首先,须要最大化利用资源,容器比虚拟机更节省资源。其次,容器的沙盒化更容易进程间相互隔离,安全性增长。
时间到了2020年了,接下来我也想让你们和我一块儿思考一下,互联网下一步的发展方向该看重什么?
数字金融,数字货币、区块链正在逐渐走进咱们的生活。
世界的不肯定性,更加促进了互联网的发展,一场疫情,老师都变成主播了,以前咱们程序员最期待的远程办公,也忽然实现了。
你会发现,各类软件和数字产品,正在侵入咱们的平常生活。
然而,咱们的互联网大厦倒是漏洞百出。黑产盛行,网站被破解,隐私被卖,网络诈骗横行,数字货币被盗,智能门锁被破解,汽车被控制。安全,成为了当下,以及将来发展的重中之重。
咱们的互联网世界,急需改变。表面上看似平静,实则暗流涌动。
有人的地方就有Bug,由于咱们的大脑没法作到机器那样的精确思考,总会出现盲区。
非也,在2019年2月,微软安全响应中心的工程师在以色列举行的Bluehat会议上,发表过一篇演讲:《微软:70%的安全漏洞都是内存安全问题》。
咱们不是要解决全部的Bug,也许将来人工智能有高度发展之后可能会作到?但如今咱们能作的,就是尽量地去消灭能够被消灭的问题。好比这个内存安全问题。
安全的世界,是须要付出代价创造出来的,而不是它本身来的。
至于咱们要付出什么代价,先按下不表,咱们接着日后面讲。
咱们人类之因此能发展到如今,是由于老是存在一些有远见的非凡人物,为咱们看清和指明方向。
在2006年,Mozilla雇员 Graydon Hore,意识到了咱们刚才讲的那个问题:将来的互联网,必定会同时注重安全和性能。性能确定是不断的追求。可是安全,才刚刚开始被注重。
Graydon 做为一名职业的编程语言,平常工做就是给其余语言开发编译器和工具集,长此以往,他其实早已萌生了本身开发一门编程语言的想法。
这门编程语言,必需要承载他对将来互联网世界的愿景。也就是,内存和性能兼备。
而且,他在平常和众多语言打交道的日子里,看到了不少非主流语言里也包含了优秀的特性,他不想让这些语言的优秀特性被埋没。
-
-
-
Rust 语言的Logo就是右侧像齿轮同样的图案。
“Rust”这个名字包含了 GH 对这门语言的预期。在天然界有一种叫做锈菌(Rust Fungi)的真菌,这种真菌寄生于植物中,引起病害,并且号称“本世纪最可怕的生态病害”之一。这种真菌的生命力很是顽强,其在生命周期内能够产生多达 5 种孢子类型,这 5 种生命形态还能够相互转化,若是用软件术语来描述这种特性,那就是“鲁棒性超强”。能够回想一下 Rust 的 Logo 形状(如图 1-1 所示),Logo 上面有 5 个圆圈,也和锈菌这 5 种生命形态相对应,暗示了 Rust 语言的鲁棒性也超强。“Rust”也有“铁锈”的意思,暗合“裸金属”之意,表明了 Rust 的系统级编程语言属性,有直接操做底层硬件的能力。此外,“Rust”在字形组合上也糅合了“Trust”和“Robust”,暗示了“信任”与“鲁棒性”。
Rust 名字起的这么好,语言目标也很是使人激动,那么, Rust 是如何作到这个目标的呢?

首先,和 Python 为表明性的动态语言相比,Rust有如下优势:
-
-
-
-
-
-
-
-
-
-
-
-
不会抛出ConcurrentModification异常
-
-
拥有一致的构建系统和依赖管理,Java的则不少可选项
-
-
-
-
-
-
和 Go 语言相比,优点在于:
无GC 暂停
无空指针
更优雅的错误处理
安全并发
更健壮的类型系统
零成本抽象
统一的依赖管理
-
-
-
-
-
-
-
-
有的人可能会认为:不是编程语言的问题,而是写代码的人水平不够,才出现这种安全问题。
然而,没有程序员是全能的,Rust 编译器捕获的错误,有可能超出程序员的经验以外。难道开车上路,有更好的司机,就不须要安全带了吗?非也。这个世界变得愈来愈复杂,咱们须要像 Rust 这样带有安全防御的语言来防止错误,保证程序的正确性。
你们可能会以为 Rust 语言功能如此强大,它的设计是否是很复杂呢?
Rut其实没有那么复杂,首先它遵循有一套本身的设计哲学:
-
-
-
-
因此,Rust 语言的总体架构,必须遵循上述哲学。
可是Rust语言为了保持简洁,它采用了基于类型系统的语言架构。
在类型系统之上,包含了一层语义,即全部权语义,以及承载了两种编程范式,OOP和FP。
在类型系统之下,包含了一个半自动内存管理系统,来自于C++ 11 引入的RAII,基于栈来半自动管理堆。
Rust编译器会根据类型系统,在编译期进行各类安全检查、展开抽象等等。
这种语言架构,致使的结果就是:要求开发者对于Rust语言的心智模型,必须和编译器达成一致。若是不一致,那么开发的时候,就会遇到编译器的阻拦。
开发者,必需要先学会 Rust,而且在写代码以前,作好类型设计,才能更高效地利用 Rust 产出正确的代码。
接下来,咱们看一些代码来让你们感觉一下 Rust 的特性。
Rust 中使用 let 来声明一个绑定(变量),上面的 x 意味着它绑定了一块内存区域,它对这块内存区域有全部权。
第二行表明,x 将它的全部权交给了 y,这在 Rust 里面叫作「全部权转移」。
第三行,调用 drop 函数想释放 x 绑定的内存区域,可是 Rust 编译器不会让这行代码经过编译。
这是由于 ,Vec 是默认存储在堆内存。假设,若是不发生全部权转移的话,在栈内存上面,将出现同时指向 Vec 堆内存的两个指针,就会发生「双重释放」同一块内存区域的安全问题。
在C/Cpp中,处理这类问题,通常都是靠开发者自己的能力来决定,可是在 Rust 里,无论是什么水平的开发者,必须先经过编译器这一关。这就有效保证了程序的正确性。而且这一切都是在编译期完成的,而非运行时检查。
Rust 也经过编译期借用检查来保证不会出现悬垂指针的问题。
上面代码中,定义了 x 可变绑定(绑定默认不可变,此处经过 mut 修饰符显式指定为可变),它的值是 Vec 动态数组。
第二行,定义了 first 绑定,它的值是指向 x 数组第一位元素的引用。
在最后一行的打印语句中,想要对 first 进行解引用操做,可是这个时候 Rust 编译器会阻止代码经过编译。这是由于,first 是对 x 的引用,可是 x 的全部权已经被转移了,first 就自动失效。 若是 first 没有自动失效会发生什么呢?它可能会变成一个野指针。
Rust 中绑定默认不可变。上图代码中前两行会正常编译,可是最后一行就不能正常编译了。
这是由于,push 方法会自动对 v 进行可变借用,此处须要的是 &mut v,可是由于 v 是不可变绑定,因此此处没法进行可变借用,从而没法对数组进行 push 修改。
默认不可变,显式指定可变。能够帮助开发者在写代码的过程当中,就想清楚状态如何改变,从而在代码设计层面必定程度上来保证正确性。
这段代码和上面的代码同理。显式的可变,会加强代码的可读性,提升代码的正确性。
Rust 默认线程安全。由于 Rust 的类型系统,能够经过 Send 和 Sync 这两个 trait 对类型进行鉴别,哪些类型是能够在线程间安全传递的,哪些不能够。
上面代码中的 Rc,就是非原子类型的,因此它没有被实现 Send 和 Sync,因此它不是线程安全的类型。在上面代码中,编译器不会让第三和第四行代码经过编译。
相反,Arc 则原子类型的,它实现了 Send 和 Sync,那么 Rust 编译器就能在编译期识别它是线程安全的类型。
1. std::thread::spawn 是建立了一个子线程,而且在该线程中执行了闭包,该闭包中使用了 v.push 为数组中插入一个数字。试想一下,若是这行代码经过编译,会发生什么事情?主线程中最后会调用 pop 方法,可是由于线程间其实并不一样步, pop出来的元素极可能不是 42 。这是线程不安全的代码。因此, Rust 编译器会阻止代码经过编译。Rust 编译器实际上是经过全部权机制进行判断,v 此时被传入了闭包中,可是它是一个可变借用。由于子线程极可能比主线程运行时间长,当主线程这边执行完了代码, v 极可能会被销毁,可是闭包中还存有 v 的引用,那这就是一个悬垂指针,这是 Rust 决不能容许发生的事情。
2. v 是可变绑定, 在子线程中的闭包中,被可变借用了一次。可是在主线程尾部代码的 pop 方法,其实也会对 v 进行可变借用。在 Rust 中,可变借用是独占的,也就是说,不能同时对 v 进行两次或屡次可变借用。不然,v 就会被修改出非预期的值。
看到了吗?Rust 的全部权机制,完美地在编译期发现了线程安全问题。
在上面代码中,Option<T> 是一个枚举(enum)类型,这实际上是一个代数数据类型中的「和(sum)类型」。
你能够用「加法原理」来简单地理解该类型,也就是说,完成一件任务,存在 A 或 B 等多种办法,选其中一种便可完成该任务。
同理,对于 Option<T> 类型,存在 Some(T) 和 None 两个值,前者表明「有」,后者表明「无」。也就是说, Option<T> 这个类型,表明一种「可选」类型。
因此,在倒数第三行代码中, find 方法返回的应该是一个 Option<T> 类型,由于在数组中搜索大于等于42的数,是有可能搜不到的。因此,这个结果会同时存在两个值,要么是有值,要么是没有值。因此,当你要使用 v 的时候,你要经过模式匹配(match)来判断,究竟是 Some(T) 仍是 None。这就强制了开发者去处理 None 的状况,有效避免了空指针。
上面 Result<T, E> 和 Option<T>,是类似的。只不过 Result 是用于错误处理,而 Option 用于消除失败。这也体现了 Rust 错误处理的一种精致的哲学,不像其余语言那样只用一个异常就包括了开发过程当中的全部问题。在 Rust 里,你能够分状况用不一样的类型来处理错误。
而且 Rust 经过引入代数数据类型,为开发者提供了优雅的错误处理方法。
Rust 也为开发者提供了底层内存分配的控制能力。
Rust 默认在栈上存储数据,可是你也能够经过 Box<T> 这样的智能指针显式地在堆上分配内存。
并且能够看具体的场景,选择适合你的全局内存分配器。
你能够经过泛型来静态分发,也能经过 trait 对象进行动态分发。
trait 是对 类型 行为的抽象。它有四个做用:
3. 标签。好比 Copy 、 Size、Sync、Send等,其中 Copy trait,可让编译器来识别哪些类型是能够安全存储到栈上的,哪些又必须是堆上,若是在堆上,就能自动Move全部权。
trait 是 Rust 的灵魂。trait 也是 Rust 对 组合优于继承 理念的一种体现。
由于 Rust 还在成长中,其 AST(抽象语法树)还在频繁地变化,因此 Rust 将 Token (分词)接口 稳定了出来,宏就基于 Token 处理。
Rust 如今支持声明宏和过程宏,用于知足不一样的需求,目前过程宏还在不断地完善和优化,目前还在等程宏属性宏的稳定。
Rust 本质上能够分为「Safe Rust」和 「Unsafe Rust」。
这是由于,如前文所说,咱们整个互联网的世界就是创建在一个 Unsafe 的基础上。咱们必须拥抱 Unsafe,才能创建 Safe 的理想世界。
所谓 Safe,其实就是 编译器对你说:Trust me this time。
所谓 Unsafe,其实就是 你对编译器和其余开发者说:Trust me thist time。而且,也意味着,其余开发者对你说:trust you this time。
不须要对 Unsafe 感到恐惧。虽然如今 Unsafe Rust 还缺少一些开发规范,可是 Rust 团队还在努力,未来也会经过 Miri 的形式,在Unsafe Rust 中加入 UB 自动检查,到时候 Unsafe Rust 将更加有保证。
Rust 中还提供了零开销的 FFI ,能够轻松经过 C-ABI 来访问任何函数,也能够经过 C-ABI 来公开 Rust 的函数和类型。
说到这里,Rust 的特性基本差很少了, Rust 还有不少特性等待你去探索。

只有在了解 Rust的特性以后,才能选择合适的场景去应用它。
在了解Rust语言特性以后,第一印象是,Rust 语言是冲着C/C++的应用领域而去的。
但其实也不只仅如此,由于 Rust 语言包含了不少现代语言特性,有高度抽象的表达能力,它其实也适合 Java、python、Andriod 、Swift的应用领域。
再加上如今 WebAssembly 的支持,Rust 也能应用于前端开发。
本质上, Rust是一个真正的全栈语言,适用于嵌入式、操做系统、网络服务、应用开发、游戏开发等等领域。
由于C语言当时开创了一个互联网时代,那么 Rust 既然拥有C/C++的能力,是否是也能够开创一个新时代呢?
这里列出了一些相对比较知名的 Rust 实现的产品。
这些项目或产品,都有一个共同的特色:它们都使用了 Rust 来创造或应对将来。
好比,Goolge 的 Fuchsia 操做系统,它有大概近 50% 的代码是 Rust 实现的,主要用于网络层。为何要从新写个操做系统?咱们前面说过了,操做系统到了它的瓶颈,无论你有多么喜欢 Unix 的设计哲学,可是时代在变化。
包括 Redox,是纯 Rust 实现的下一代安全的操做系统,背后的商业公司是 System76 ,它的理念是「一切皆 URL」。
TockOS,则是纯 Rust 实现的嵌入式实时操做系统,最近已经用于 Google 的加密硬件设备产品中。
Bottlerocket 则是亚马逊纯 Rust 实现的基于 Linux 的专门承载容器的操做系统,专一于安全性和可维护性,提供可靠、一致的服务,以及基于容器的工做负载的安全平台。这是亚马逊多年云平台产品积累的经验,由于它们也看到将来安全和性能兼备的重要性。
国产分布式 New Sql 数据库 TiDB 的底层分布式存储 TiKV,也是 Rust 实现的。还有国产的区块链公链项目 NervOS,也是用 Rust 实现的。
还有不少优秀的项目,它们都使用了 Rust。此处就不一一列举了。
在 Rust 的官方页面( https://www.rust-lang.org/zh-CN/production/users )上,能够看到在生产环境中使用 Rust 的公司。上面只是列举了一部分,其中包括了国内的字节跳动(飞书)、PingCAP(TiKV)、秘猿(NervOS、Cita)三家公司,其实还有知乎、淘宝,也都在使用 Rust。未来你们必定也还会看到更多使用 Rust 的公司。
可是,当前 Rust 也存在一些缺点,阻碍了不少公司想要使用它的决策。
这是流传最多的一个缘由。Rust 的设计目标和特性,决定了 Rust 是一个进入高门槛的编程语言。可是这并不意味着 Rust 是一个复杂的语言。相反, Rust 是一门简洁的语言。Rust 语言的设计高度一致性,足以让开发者抓住它简洁的内核,创建和 Rust 编译器相匹配的心智(编程)模型。固然,这须要开发者,下必定的功夫。
2. Rust 编译慢。这一样是 Rust 语言设计目标引发的问题,由于 Rust 要在编译期完成各类安全检查、展开抽象等功能。幸运的是,Rust 官方团队也在逐渐改进这个问题。好比支持增量、并发编译等功能。
3. Rust 生态还在成长过程当中。 好比最使人期待的异步开发功能,才稳定没多久,生态还在成长,这可能会给某些团队带来引入的困难。
4. 有些语言特性还待完善,好比 Const Genric 、特化等。
还有一点须要注意,Rust 并非默认就是高性能的代码,还须要开发者本身优化。

那么,当地大学生学习 Rust 有什么意义呢?难道学习C/Cpp/Java还不够吗?

Rust 和 C/ Cpp/ Java的比较前面已经说过了。我认为, 大学生学习 Rust 的意义有如下几点?
1. 跟进时代变革的脚步。学习 Rust 能够紧跟技术的前沿,了解相关领域的发展动态。
2. 无阻碍地培养本身的全栈能力。Rust 语言自己就有全栈能力,你掌握了 Rust 语言,就不用把本身束缚到某个领域中,由于 Rust 在操做系统、数据库、网络服务、嵌入式、前端开发等等,都有应用,你要切入某个领域,就不须要再多学一门编程语言了,只须要掌握那个领域的领域知识便可。
3. 打造良好的编程基础和思惟习惯。Rust 是当下惟一一门,抽象表达能力比肩高级动态语言,性能和底层控制能力比肩C/C++的语言,学习 Rust 的过程当中,会把你对于底层操做系统、网络、范式抽象、设计模式等基础都训练一遍。若是你不得不学其余语言,有了这些基础,你会很快掌握其余语言。而且,在 Rust 编译器的打磨下,你能拥有一个良好的思惟习惯。
4. 成为更好的开发者。
为何这么说呢?由于 Rust 编译器的存在,可让你养成:
2. 对内存安全、线程安全等基础创建一个系统的认知,能够高效地产出正确的程序。
对于企业来讲,那你就是一个合格且更优秀的开发者。
学习 Rust 让我从新爱上了编程,使用 Rust 让我感到骄傲和自豪。我也但愿能把这门优秀的语言,推广给更多的人,尤为是高校的广大同窗们。
感谢阅读。
点击阅读原文,可直达 B 站视频回放页面。