为什么我说Rust是靠谱的编程语言

为什么我说Rust是靠谱的编程语言

2016年2月22日修补说明:本文最初发表至今已逾半年,这期间Rust语言蓬勃发展,版本从1.0升级到了1.6(1.7也近在咫尺),变化很大。现借《程序员》杂志3月刊发行之际对原文做一些修订和增补,其中增补部分在文中有明确标记。

序言:本文试图帮您解答“我要不要(投入大量时间和精力)学习Rust语言这个问题。作者尽量较少的谈及Rust语言本身,反而尝试从Rust语言周边入手,长时间、大范围、多角度地考察,研判Rust语言是否靠谱,并给出尽可能客观的理由。为写成本文,作者Liigo不惜“卧底”Rust“老巢”长达一年多,收集整理总结了大量信息。如果嫌长,可以只看小标题,粗略浏览一番。

1. Rust编程语言

Rust(blog)是一门强调安全、并发、高效的系统编程语言。其中四个关键词,系统编程、安全、并发、高效,是Rust语言的核心特征,也是区别于其他编程语言的首要因素。

  • Memory safety without garbage collection

  • Concurrency without data races

  • Abstraction without overhead

除此之外,我再补充一些关键词,以便读者更直观地了解Rust:静态类型/编译式语言/静态编译/动态编译、泛型/函数式/面向对象、模式匹配/ADT、DST/Associated Types/闭包(Closures)、Static/Dynamic/Multiple-Dispatch、 没有虚拟机(VM)、没有垃圾收集器(GC)、没有运行时(Runtime)、没有空指针/野指针/内存越界/缓冲区溢出/段错误、没有数据竞争(Data Race)……

Rust语言具有特性丰富、设计优良、适用范围广等诸多优点。

我(Liigo)从2013年底开始正式关注Rust项目,……至今有一年半了。其中有赞有批,有争有闹,也有贡献源码。本文所写的是我这些日子以来的所看、所闻、所感。

判断一门新的编程语言“是否靠谱”,是主观性很大的课题。Rust语言今日才刚刚发布1.0版本,它的未来发展走向如何,谁也说不清楚,说到底都是猜测。但是直觉告诉我,如果人靠谱、团队靠谱、技术能力靠谱、态度靠谱、社区靠谱,这个项目在很大程度上就是靠谱的、值得期待的。

谨以此文,献给我长久期待的 Rust 1.0

2. 开放、友好、高效的开源社区

相当彻底的开源项目,开放、透明、友好,进度热火朝天,动作大刀阔斧。这是我第一次亲身参与并观察到的如此大规模的开源编程语言项目的开发过程。(之前也关注过Go语言项目,但其规模要小得多。)

  • 开放源代码、GitHub/Git在线开发 https://github.com/rust-lang/rust

  • 开放系统设计过程,重要设计项目的提出、讨论、评估、决策均在线进行(RFCs)

  • 内部决策过程也公开透明,每周发布会议记录(meetimg-minutes)

  • 公开接受第三方开发者提交的 Pull Requests,必要时还指导开发

  • 有一个核心团队(the core team)负责项目的发展方向和最终决策

  • 有大量的(超过 1000 人!)第三方开发者给Rust贡献源代码、文档和测试用例

  • 多次将优秀的第三方开发者吸纳进入官方开发团队和核心团队

  • 多次在世界各地(包括北京)主办和协办小型本地开发者见面会

  • IRC,internals,Reddit/r/rust,HN(Hacker News), StackOverflow有许多跟Rust相关的技术讨论,常见核心开发者参与其中

  • Github上用Rust语言开发的项目也风风火火(详见下文)

热火朝天”,用于形容其工程量大、进度快、成效显著。看看Rust的 Release Notes,常见这类字眼:

  • Version 1.0.0 (May 2015): ~1500 changes, numerous bugfixes

  • Version 1.0.0-alpha.2 (February 2015): ~1300 changes, numerous bugfixes

  • Version 1.0.0-alpha (January 2015): ~2400 changes, numerous bugfixes

  • Version 0.12.0 (October 2014): ~1900 changes, numerous bugfixes

  • Version 0.11.0 (July 2014): ~1700 changes, numerous bugfixes

  • Version 0.10 (April 2014): ~1500 changes, numerous bugfixes

  • Version 0.9 (January 2014): ~1800 changes, numerous bugfixes

  • Version 0.8 (September 2013): ~2200 changes, numerous bugfixes

  • …/p>

还有很多就不一一列出了。Rust自从2012年1月20日发布0.1版,到现在的1.0正式版,平均每隔三个月发布一版,每次升级改进都多达一两千项。 Rust是在GitHub/Git上在线开发的,开发者对源代码的每一项改动(commits)都被版本管理系统记录在案,有据可查。 大多数是实质性的功能升级,不存在靠修改标点、配置文件、拼写错误、或者每改三五行代码就提交的凑数行为(在此严重鄙视Atom编辑器、Kotlin语言)。 反而常见动不动就 甚至 行 Rust 代码的情况,

有一个网站叫 “This Week in Rust”,每周进展公告,专门介绍Rust语言最近一周的最新重大变动,可供大家参考。里面的内容虽然不全,但颇有代表性。这个网站几经变迁,但是自2013年6月上线时起,两年来,作者cmr坚持每周更新,几乎从未中断。

总之我(Liigo)说Rust的开发“热火朝天”,一点也不夸张。(我还要顺便鄙视Apple公司,发布某某OS新版的时候,动不动就说“新增1500项API”,哎,咱还就较真儿了,去查,算上所有Function、所有Type、Type内的所有Method、所有常量定义—— #define XX 123,你懂的 ——勉强够数!跟Rust的 “~1500 changes” 相比,天上人间。)

大家可以比照近十年在中国大陆和近两年在南中国海的大型基建项目的进展速度,体会Rust那种热火朝天的开发场面。处于这样的开发环境下,所有人都深受鼓舞,所有人都知道,一切都会越来越好,知道所有的欠缺和困难都是暂时的。行动永远比语言有说服力。

大刀阔斧”,用于形容其开发行为豪迈、干脆,持续追求“更好的设计和实现”,无包袱、无迁就。 只要有了更好的设计(或实现),经过深入讨论和评估之后,果断替换掉旧的设计(或实现)。 不管这项改动有多复杂、多麻烦,也不理会因此形成的大量不兼容性,勇往直前的行动。 说去胳膊就去胳膊,说卸腿就卸腿,就是眼睛它也敢摘下来镶个火眼金睛再装回去。牵一发而动全身的事情,干了可不止一次。 例子有很多,最典型的是libgreen,之前Rust靠它一条腿走路,后来有了libnative,两条腿走路,再后来全都砍掉,最终做到没有运行时(Runtime)。 其他的例子还有std::io/os/path的大型重构(I/O OSPath Reform),重新设计然后无缝替换回去(有rustc、servo为证),可谓自己给自己做了大型手术。 一个新兴的项目,在真正面世之前的开发过程中,如果不经历几次这类大的阵痛,很难说它最终是成熟的和久经考验的。 如果因为各种顾虑而迁就、保留旧的设计(或实现),很难说最终的产品是完善的和设计优良的。 如果面世之前都不肯改进,面世定型之后改进的可能性就更小了(束手缚脚),缺陷可能伴随终生,令所有的用户长期的纠结。 (这种大刀阔斧,与我(Liigo)当年在易语言公司参与研发EF语言时我所主张并践行的观点一致。有意推迟发布1.0也是为了给完善产品留足充裕的时间。)

需要强调的是,Rust开发者大刀阔斧的日子,只发生在Rust 1.0正式版发布之前。1.0之后,他们将老老实实的遵守 SemVer 2.0 规范,再也不会轻易做出破坏代码向后兼容性的行为。本文后面还会谈到这一点。

3. 十年勤耕不辍、低调做人高调做事

2006年,创始人Graydon Hoare启动了Rust编程语言项目,利于业余时间开发了三年,完成Rust语言的第一个可用版本。早期的Rust编译器 rustboot 是用OCaml语言开发的。这三年间发生的事情,我们现在知道的并不多。根据创始人对Rust的命名,可以一窥其设计初衷:

我从很多名称中挑选出了”Rust”。主要受到病原菌的启发——那些令人惊讶的、生死轮回的、多源寄生的奇妙物种。而且”Rust”还有其他内涵:它很好地契合了“贴近金属”(bare metal)、“复古编程语言技术”的主题;它从字面上融合了”Trust”(信任)和”Robust”(健壮)。 —— Graydon Hoare

2009年,Rust项目被Graydon Hoare赠送给(英文原文”was presented to”)Mozilla公司,并得到持续活跃地开发支持。Mozilla创建了以Graydon为首的专业团队全职开发Rust,并且开放源代码。

2010年至2011年,Rust语言的编译器被用Rust语言重写,完成自举,采用LLVM作为编译后端——之前的Rust编译器是用OCaml语言编写的。

2012年1月发布0.1版,第一个面向公众的预览版本问世。从那时起,平均每3个月发布一个新版本,版本号每次只加0.1,改动的内容却往往天翻地覆。2012年到2015年这三年多的时间里,开发者持续不断的改进Rust语言、编译器和标准库,翻新重修,精益求精。不断地否定自己,不断地超越自己,一步一个里程碑。

2012年2月,也就是Rust 0.1刚刚发布不久, Servo项目被创建,用Rust语言开发下一代浏览器引擎,目的在于验证Rust语言开发大型实用项目的能力。

2012年到2015年这三年多时间,Rust和Servo互相扶持着茁壮成长起来了。Mozilla公司在Rust和Servo这两个不盈利的开源项目上付出的大量人力物力财力和时间成本,体现了他们全力打造下一代全新系统编程语言的决心和气魄。但是他们很少花费精力在大规模宣传和营销上,导致大部分公众/开发者几乎从没听过Rust语言,仅靠长期的口碑相传吸引了一批慧眼识金的忠实的开发者。

Rust不是富二代也不是官二代,它没有一个有钱或有权的爹。Mozilla公司,论资金规模和影响力,远远不及Google、Apple、Microsoft,勉强算是二流公司,但是它依然给予了Rust长期的坚定的支持。

历经近十年精心打造,Rust靠自身实力赢得未来。2015年5月16日,Rust正式发布1.0版本,破茧成蝶,吹响进入新时代的号角。

4. 定位精准而潜力广泛的应用领域

现今的软件系统开发,从底层到中层到上层,大致分为以下三个层次:

  • (底层)系统底层开发:裸金属(bare metal)、操作系统(OS)、内核(kernel)、内核模块(mod)等

  • (中层)系统应用开发:虚拟机(VM)、容器(Container)、数据库/游戏/Web/Ftp/Dns服务器、浏览器引擎、模拟器等

  • (上层)普通应用开发:编译器、浏览器、消息推送系统、Web应用系统、管理信息系统、其他等等

其中,(底层)系统底层开发,强调对底层硬件的控制;(中层)系统应用开发,对CPU和内存的占用十分敏感;(上层)普通应用开发,更倾向于方便快捷的开发效率。通常意义上所说的“系统编程”,往往是指中底层系统开发。

Liigo认为,Rust语言足以胜任这三个层次的软件开发。理由是:Rust是静态类型的编译式语言,基于LLVM生成高度优化的代码,再加上没有垃圾收集器(GC)等额外的运行时开销,执行效率非常高,对内存的利用十分灵活,因而胜任(中层)系统应用开发;Rust具有丰富的语言特性,便捷的项目编译和依赖管理,充分可用的跨平台的标准库,因而胜任(上层)普通应用开发;Rust支持raw pointer、unsafte block、C ffi、asm!、No runtime,因而胜任(底层)系统底层开发。Rust语言特别强调并保证的内存安全,对于三个层次尤其是中底层,是额外的突出的加分项。

上面的论述偏向于理论,可能不如实践有说服力。下面我们看看现实中Rust已经做到了什么程度(不是能做什么,而是做了什么,咱们靠事实说话):

  • 底层开发: rustbootrustosbarebonesreenix5stepsothers

  • 中层开发: Servo浏览器引擎Piston游戏引擎HyperHTTP服务器SprocketNESNES模拟器LlamaDB数据库

  • 上层开发: Rustc编译器 Cargo项目管理 Iron&NickelWeb开发框架ConrodGUI库

早在2013年我(Liigo)开始关注Rust之前,那时候Rust还有可选的GC,还有不算小的Runtime,还有笨重的标准库。即使在那种情况下,都有人不断地尝试用Rust做底层开发(参见前面的链接)。后来,Rust有了几个大的动作,令其更加胜任系统底层开发工作:

  • 拆分标准库(std),提取出专门针对底层开发的极其轻量级的核心库(core) RFC #40 Issue #13851: Make std a facade PR #13901: Refactor libcore out of libstd old

  • 移除运行时库 RFC #230 PR #18967: Finish runtime removal PR #17673: remove librustuv PR #18557: remove rtio PR #19654: remove librustrt

  • 删掉GC RFC #256 PR #17666

  • 放弃分段式栈 Abandoning segmented stacks in Rust

  • 支持静态编译 PR #10528: Add static linking to rust

  • 添加#![no_std]属性no_std 允许用户放弃使用(功能丰富但相对笨重的)标准库(std),选用更底层更轻量级的core,libc,alloc,collections 等库,Option/Result/Iterator/Deref/Cell/String/Vec/HashMap等基础类型依然可用。更绝的是,你甚至可以连core也不用,纯裸奔(参见本人旧作)。由此可见Rust为支持底层开发不遗余力。能做到这一地步的系统语言还真不多见。

Rust是名副其实的系统编程语言,在这个领域,它不惧怕跟任何对手竞争。向下,Rust可取代C语言地位;居中,Rust可挑战C++市场,向上,Rust可向Java、Python分一杯羹。总之,Rust精准定位于中底层系统应用开发,上可攻下可守,适用范围相当广泛,具有全能型选手的潜质。开发者们学习Rust语言,不怕没有用武之地。

5. 自举(用Rust语言开发Rust编译器)

Rust在2010年至2011年完成自举,使用Rust语言开发出Rust编译器rustc, 取代了之前用OCaml语言开发的Rust编译器rustboot。Rust标准库,很早就是Rust语言写的。这意味着,早在四年前,Rust早期核心开发人员,就已经是全职的Rust程序员了,一天八小时,几乎完全使用Rust语言编程:用Rust开发标准库(和其他库),用Rust调用标准库开发编译器,用编译器编译标准库和编译器。等到1.0发布时,他们已经是具有多年极其丰富的Rust开发经验的程序员,这期间他们积累的大量设计开发经验和教训,无疑不断地推进了Rust自身的迭代更新。

有人说:没有必要自举,不自举不代表它没有这个能力。这话说的没有太大毛病。但是我们考虑如下两点:

  • 1、自举从事实上印证了它(编程语言+编译器)具备这个(强大的)能力,不自举只能在理论上保留它具有这个能力的可能性,两者不在一个层面上,说服力孰强孰弱不言而喻。新语言在大规模推广之前,往往欠缺这种说服力,进而导致推广不利,陷入怪圈不能自拔。

  • 2、自举过程中和自举之后,核心开发者每天使用自己开发的语言工作(开发自己的编译器),不断的在实践中锻造,利于及早发现设计缺陷和不足之处,并及时解决;自举之前,只能每天花费大量的时间和精力,使用其他编程语言开发和维护自己的编译器,学习积累的都是别的语言的经验和教训,缺少在实践中检验自己设计的语言的机会。如果自己设计的语言自己都不去深度地使用,又上哪里获取第一手的反馈信息呢,又如何改善呢。

所以自举越早对编程语言自身发展完善越有利,最好是在自身定型之前尽早自举。

在编程语言自身定型之前尽早自举,这句话说起来容易,实施起来却非常困难。语言不完善,某些功能就可能暂时没法实现;语言不稳定,需要不断的修正和改进,用它写的编译器也需要相应的大量的维护更新甚至重写,是很大的工作负担。所以很多新的编程语言的作者,不愿意(尽早)自举,相应地也就永久失去了自举带来的好处。

我(Liigo)举两个反例:

  • 第一个是Google公司的 Go语言,截止到2015年本文发表时,其编译器和运行时库,包括语言核心数据结构和算法map、channel、scheduler等等,还是用C语言开发的(补记:2015年8月1.5版本之后才替换为Go)——他们的核心开发人员真正用自己开发的Go语言进行实际的大型应用开发的机会并不多。虽然标准库是用Go语言自己写的,但他们却没有大范围使用标准库的经历。实际上,他们缺少使用Go语言的实战开发经验,往往不知道处于开发第一线的用户真正需要什么,无法做到设身处地为程序员着想。缺少使用Go语言的亲身经历,也意味着他们不能在日常开发中,及时发现和改进Go语言设计上的缺陷和不足。

  • 第二个反例是中文编程的翘楚 —— 易语言,它的编译器、核心支持库、集成开发环境(IDE),和绝大多数支持库,都是用C语言或C++语言开发的,他们的主要开发人员,包括创始人吴涛在内,每天的工作是熟练编写大量C/C++代码,写易语言代码的机会少之又少,即使有也是写一些调用支持库的示例这类浅尝辄止的代码。事实上在易语言研发部,只有庄晓立(Liigo)、袁晓辉(海洋)、龚辟愚(GBB)等少数几位开发者具有较深的易语言功底,其他开发人员进入研发部之前甚至都没听过易语言。吴涛本人早年还写过一些较大的易语言程序,代表作是俄罗斯方块(代码多达500行),后期也很少写了,偶尔写一些Sample代码作为支持库的示例(往往不超过100行)。易语言公司里写易语言代码最多的应该是项目教育培训等部的史世恒、潘春华、季翔、王军、张志恒他们。后来易语言暴露出来一些大的设计缺陷,已经不好修复了,除非伤筋动骨地翻修(放弃向后兼容性)。再后来吴涛组织团队又开发了全新的编程语言“易语言.飞扬”(EF),发布之前用EF语言开发了自己的集成开发环境(IDE),是一大进步,但依然没有完成自举。后来听说吴涛自己一个人在搞3D游戏引擎(Volcano3D),气场强大,希望推出真实的游戏(而非demo)检验其优秀品质。

而Rust语言,偏偏克服了诸多困难,提前4年完成自举,成为是最成功的案例之一,充分的享受了自举的好处,不断地在实践中完善了自身的设计。

没有深度的实践,就没有优秀的设计。除了自举,Rust还有其他的深度实践。

6. 两个半”大型成功案例”: servo, rustc+std, cargo

(作者Liigo注:本节以下有关代码行数的数据于2015年7月30日得到修正,详情另见《关于Servo项目中Rust代码行数的数据来源》一文。)

  • Servo: 下一代浏览器渲染引擎(类Webkit/Blink),超过25万行Rust代码

Servo是Mozilla公司另外一个独立开发组的项目,启动于2012年初,跟Rust并行开发。多年来,在Rust语言从幼年到少年逐步成长、长期剧烈变动的情况下,Servo居然还能保持正常开发进度,实属不易。Patrick Walton在Servo开发组和Rust开发组都是核心程序员,Servo组的Lars Bergstrom经常参加Rust组的每周会议,这保证了两部门的有效沟通。Rust组经常会优先协助解决Servo组遇到的问题,维持Servo开发任务正常推进。

对于Rust而言,Servo项目存在的最大意义就是,它实践并印证了Rust语言具有实际的大中型项目开发能力(而不仅仅停留在理论上),同时获得了珍贵的设计开发经验和教训,反过来进一步促进了Rust自身的发展。Rust语言和标准库逐步发展到今天,许多优秀的设计得以引进,许多有缺陷的设计得以改良,都要感谢Servo这类大型实践项目,在Rust 1.0之前就已经长期存在。试想,如果Rust定型之后才启动Servo项目,实践中发现语言的重大设计缺陷又能怎样,反正木已成舟,悔之晚矣。

Servo已经通过了 Acid2 标准测试;可以 并发渲染 Github/Reddit/CNN 这类大型静态网页,性能 明显高于 当前的Firefox浏览器的Gecko引擎;可以无缝替换基于Chrome的CEF 框架;已经实验性的应用在Firefox OS平台(b2s)。2015年将发布测试版浏览器。Servo有机会成为浏览器历史上里程碑式的产品。

  • rustc+std: Rust编译器和标准库,超过30万行Rust代码

时至今日,rustc负责编译全世界所有的Rust源代码,包括rustc+std的30万行和servo的25万行,以及crates.io网站上的2000多个第三方库,是名副其实的大型成功项目。

  • Cargo: Rust的package管理器,项目依赖管理

代码量相比前两者而言要小的多,所以我算它是半个成功案例。代码虽少,但实用性、流行度有过之而无不及。全世界大约99%的Rust项目采用Cargo编译。crates.io网站上有2000多个包,总下载量超150万次。Cargo最大幅度地简化了Rust项目的编译和依赖管理,可以说是目前开发Rust项目的必备工具。

7. 十分重视并认真对待1.0版本

Rust 1.0被开发者视为第一个稳定可靠的可用于生产环境的正式版本。也就是要承诺:现有的大部分语言特性和标准库API要稳定下来,以后不能轻易改变,非得要变得话也得保持向后兼容;功能上要基本全面,至少要满足基本的软件开发需求,不能有明显的欠缺;质量上要有可靠性保证,不能动不动就这里有问题那里有问题。此外,还要给语言将来的发展留足余地。官方博客Road to Rust 1.0 对于 1.0 有详细的阐述。

任何认真的新编程语言面临“何时发布1.0版”这个问题时都会感到纠结。发布的越早吧,初级产品没经过多少实际检验,用户量一上来用的人多了,很可能爆出基础设计缺陷,不得已大幅翻修,导致口碑不佳,然后无人问津,项目就算失败了;发布的越晚吧,影响力小用户量一直上不去,也得不到太多实际检验,始终达不到1.0的水平,可能就一直默默无闻下去了。Rust在1.0发布时机上把握的还算比较到位:诞生快十年,高速发展三五年,吸引了一大批用户,自身也经过的“两个半”大型项目的实际检验。要说早,肯定是不早了。Rust没有盲目的早早发布1.0(例如在2012年),是因为他们对1.0期待很高,他们对自己要求很高,他们心里有一杆秤。因为他们是认真的。

为了在2015年5月保质保量发布Rust 1.0,他们提前做了哪些工作/p>

7.1. 标准库API的稳定性

Rust为标准库内所有API,即所有fn、所有type(struct/enum/trait)、所有method、所有impl、所有const/static、所有macro_rules!,都逐一标注了稳定性标签:stable、unstable或deprecated。并且声明,1.0版本内包含的所有stable API,都将在SemVer 2.0规范下得到向后兼容性保证,今后所有1.x版本都不会破坏其稳定性(除非遇到重大BUG不得已而为之)。

我们去查Rust开发组公开的会议记录,会发现在2014年6月23日到10月1日,共有8次API review专题会议(01234567),逐一审查确定各API的稳定性标签。此后更长的时间里,又不定期的将更多API标注为stable或unstable/deprecated,在rust repo里搜索“stabilize” 可以得到大批提交记录,显示出这项系统工程显然不是一朝一夕所能完成的。

目前标准库名下stable API大约有2500条,占总数的80%。新生编程语言中能做到这个程度的,很少见。

增补:针对稳定版API的修改,怎样的修改是向后兼容的、怎样的修改是破坏兼容性的呢RFC #1105 给出了非常详尽的说明和指导。而且Rust还有一个叫Crater的工具,负责检测某项修改是否破坏了兼容性,其工作原理是,借助集成测试环境,以该项修改为基础构建临时Rust工具链,用之逐一编译crates.io网站上的三千多个Rust开源项目,只要有一个不通过,就说明该项修改存在破坏兼容性的可能。许多代码提交后首先要经过Crater的检验才可能被正式接纳(案例:PR #29498 PR #30796 )。

增补:Rust在1.0发布之前筹划实施了类似Chrome的滚动式版本升级计划(RFC #507)。nightly版本6周后升级为beta版本,beta版本6周后升级为stable版本。新提交到创库里的代码,必然经过nightly和beta两个周期,经过总共约12周的测试和过渡期,才会正式进入stable版本。许多核心开发者都是基于nightly版本工作的,还有很有用beta版本的开发者,再加上完善的travis-ci测试,基本可以保证在stable发布之前发现并修正存在的问题,确保stable版本的可靠性。

7.2. 精益求精的API设计

  • Reform: Collections reform(v1: RFC #235来源:Liigo

    声明:本站部分文章及图片转载于互联网,内容版权归原作者所有,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

上一篇 2015年4月13日
下一篇 2015年4月13日

相关推荐