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

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

作者:Liigo(庄晓立)
时间:2015年5月16日
原创链接:http://blog.csdn.net/liigo/article/details/45757123
版权声明:未经作者许可不得转载;授权转载需注明出处。

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

1. Rust编程语言

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

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

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

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

  • 有大量的(超过 1000 人!)第三方开发者给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 OS Path 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已经做到了什么程度(不是能做什么,而是做了什么,咱们靠事实说话):

    早在2013年我(Liigo)开始关注Rust之前,那时候Rust还有可选的GC,还有不算小的Runtime,还有笨重的标准库。即使在那种情况下,都有人不断地尝试用Rust做底层开发(参见前面的链接)。后来,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语言开发的(正陆续替换为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

    • Servo: 下一代浏览器渲染引擎(类Webkit/Blink),超过40万行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编译器和标准库,超过35万行Rust代码

    时至今日,rustc负责编译全世界所有的Rust源代码,包括rustc+std的35万行和servo的40万行,以及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,他们提前做了哪些工作?

    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专题会议(012 3 4 5 6 7),逐一审查确定各API的稳定性标签。此后更长的时间里,又不定期的将更多API标注为stable或unstable/deprecated,在rust repo里搜索"stabilize" 可以得到大批提交记录,显示出这项系统工程显然不是一朝一夕所能完成的。

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

    7.3. 精益求精的类型系统设计

    7.4. 精益求精的文档和代码复审

    7.5. 1.0之后的开发计划

    1.0是起点而不是终点,1.0之后Rust还将持续不断地开发新的语言特性,打造更完善的标准库。

    核心开发者Niko Matsakis在今年4月份发表 Priorities after 1.0 一文,详细阐述了1.0之后的开发任务、计划、优先级,内容很多却安排有序,体现了他们对这些问题的深度思考。此文在开发者社区中引起强烈反响,获得热烈讨论。Niko还发表了系列博客文章的第一篇"Virtual Structs Part 1: Where Rust’s Enum Shines"

    8. 精心设计的规范透明的开发流程

    在2014年3月之前,Rust开发组并没有十分规范的开发流程,基本过程是这样:修改代码,提交PR,Review,Merger。这样导致的问题是,没有形成设计文档,一旦遇到较大规模的代码改动,别人想理解他的设计思路,首先要读懂他的代码,这给方案评估、代码评审制造了困难,而且容易形成无人理解或难于维护的代码。

    从2014年3月开始,Rust引入了规范化的RFC流程。规定,对语言重大改动之前,需先提交RFC文档,写明包括意图、详细设计、优缺点等在内的完整技术方案,供社区集体讨论,最后提交到Rust核心开发组每周的专题会议上评估审核,获得批准之后才进入实施阶段(代码实现)。规范化RFC的好处是,首先形成了完整的技术文档,利于集体讨论、评估(进而优化方案),利于方案实施、后期维护,而且利于核心开发组主导项目进展方向。RFC流程实施一年来,在Rust发展过程中发挥了极其深远的作用,先后通过了一大批十分重要的RFC,有力地推动了Rust语言的革新。

    举一个例子,说明社区会主动监督开发流程的规范性和透明度:2014年8月,官方人员aturon居然想偷偷摸摸地把unwrap方法改名为assert, PR #16436: API conventions cleanup(80+),被网友 发现(120+) 后引起极大争议。争议的起因是他们工作透明度不够,事先 公示(50+) 范围不足,未得到充分讨论。最终这一改动被迫撤消。

    9. 不拘一格聘请专业技术人员

    Steve Klabnik之前写过一篇介绍Rust的入门教程 Rust for Rubyists,文风娓娓道来,深得群众喜爱。2014年2月,Rust官方人员看重了他的文档写作才华,付费 聘请 他全职为Rust 创作文档。他的主要代表作是Rust官方的 The Rust Programming Language(Rust Book),以及大量API Docs。因为其卓越贡献,steveklabnik目前已经是Rust核心开发组 成员。

    Tilde公司以前开发的Ruby包管理器Bundler在Ruby领域非常流行,其架构设计被实践证实获得成功。2014年3月,Rust官方 宣布 聘请 Tilde公司的核心技术人员Yehuda Katz和Carl Lerche,全职为Rust设计开发全新的开源的 Cargo,目标是打造世界级的包管理器("a world-class package manager for Rust")。现在Cargo已经初步获得了很大的成功,还在蓬勃发展中。因为Yehuda Katz的突出贡献,他已经成为Rust核心开发组 成员。

    深受好评的Rust学习示例网站 http://rustbyexample.com 的早期创建者 Jorge Aparicio(japaric) 后来被邀请 加入 了(Mozilla公司的)Rust官方团队。

    meeting-minutes 里面搜索 "Friend of the Tree" 或 "fott" 你会发现更多人陆续加入了Rust团队。

    10. 大规模的广泛的社区参与

    我上面列出的多是长期以来持续开发和维护的项目,这在Rust语言长期剧烈变动的情况下愈显弥足珍贵。理智的人们不会无缘无故的花费大量时间和精力。许多忠实的第三方开发者长期地投资Rust项目,体现了他们对于Rust语言的热爱和对其前景的看好。

    10.1. 社区合力完成的项目(libextra, diagnostics):

    10.1.1. 分解extra库 Issue #8784: split up libextra

    10.2.2. scoped thread:

    2015年4月11日爆出这个大BUG(Issue #24292),直接拷问Rust“内存安全”核心概念,当时距离1.0发布已不足5周时间。要知道,刚刚一天前,std::thread::scoped()还被官博 当作既安全又典雅的优秀API的典型、满怀骄傲地向全世界推介。是不幸,还是该庆幸?我认为该庆幸,有机会消除一个隐患,而不是在不知情中带着重大缺陷进入1.0。

    官方人员一味的强调 Issue #24292 是个例,一味的强调“不保证析构函数务必执行”不违反内存安全,并不能消除群众心中的不安全感。在我看来,官方人员给出的解决方案(RFC PR #1066 #1084)一个是“头痛医头”一个是“脚痛医脚”,反而是民间技术人员提交的方案(RFC PR #1085 #1094)更接地气,至少是朝群众期望的方向努力了。可能是1.0发布日期逼近,实在没有时间评估和实施其他方案,最终官方坚持通过了RFC #1066。这一次我给他们集体评负分。好在他们广泛深入地参与了所有相关的讨论,内部无争议地做出了理性决策,结果未必坏。

    10.2.3. mutable & unique:

    Rust社区对于可变性(mutable)和唯一性(unique)的反思和争议。所有这些话题,都引发了许多热烈的讨论和激烈的争论,火热程度空前。之前 reddit.com/r/rust 内评论数达到200+的极为少见。

    10.2.4. others

    • int/uint ? isize/usize:

    • integer overflow:

    • reddit/HN hot posts:

    大量的、广泛的、深入的社区技术讨论,体现了人民群众积极参与Rust开源项目的热情。多个体、多角度、多出发点的争论,有利于参与者充分认清同一个技术问题,有利于鉴别各方案的优缺点,有利于折中优选最佳可行方案。优质社区是Rust发展的基石。

    当多方争论不相上下的时候,我们发现,Rust拥有一个坚强的核心团队(the core team),总是会应急出面,充当主心骨,做出最终的理性决策,避免出现互相扯皮却始终一事无成的最坏结局。他们的实力是有目共睹的,他们的信誉是逐步赢得的,他们做出的选择,不一定是最优的,但一定也不差。



    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    ~~~~ 全文完,感谢!(翻页到这里也不容易呀,顺便吐个槽吧:)~~~~

    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。