Rust 1.0 发布一周年发展回顾与总结
前言
Rust 1.0发布刚刚一周年(2015.5~2016.5),这一年来Rust又取得了长足的进步。笔者尝试从多个方面总结过去一年来Rust领域的重要动作、进度和成就。本文内容丰富,信息量大,总结比较全面。读者从中可以看到:开发者的辛勤努力和Rust语言的快速成长,Dropbox等公司在生产环境中的核心模块应用Rust,社区成员积极参与社区活动,Rust在国内的发展状况,等等。
Rust语言/编译器/标准库升级
一些零散的升级,像添加Stable API、局部提升性能、修改某些BUG等等,在这里就不提了。我将要说的,都是影响深远的重大升级。当然,还有很多工作未最终完成,要等以后的版本问世。但是前期的研究、讨论、设计等步骤基本走完,剩下的无非就是编码实现、实验性应用、标准化等步骤,只要没有意外,后面的一切都顺理成章。
本文多次提及的RFCs,后面将有专门章节介绍,此处不展开叙述。
impl specialization(RFC 1210)
这一特性类似C++的模板特化和偏特化。允许为接口或类型定义多个可重叠的impl实现,最终由编译器依据上下文自动选择其中一个最具体、最specific(general的对立面)的实现。它能帮助程序员更好的优化性能、重用代码,还为将来实现规划已久的"efficient inheritance"提供基础支持。
举个简单的例子。Rust从1.0开始就为“实现了Display接口的任意类型T”
实现了ToString接口。这是一个泛型实现,涉及大量类型,覆盖面很广。从代码实现细节上看,用到格式化文本输出(fmt::Write::write_fmt)。
001 #[stable(feature =“rust1”, since =“1.0.0”)] 002 impl<T: fmt::Display+? Sized> ToString for T { 003 #[inline] 004 default fn to_string(&self) -> String { 005 use core::fmt::Write; 006 let mut buf = String::new(); 007 let _ = buf.write_fmt(format_args! (“{}”, self)); 008 buf.shrink_to_fit(); 009 buf 010 } 011 }
具体到基础字符串类型str,它当然也属于上面提及的“实现了Display接口的任意类型T”,因而自动实现ToString接口,拥有to_string方法。然而上面的实现代码里的格式化输出功能对于str转换至String来说是多余的,带来额外的运行时开销,因而导致"str".to_string()比"str".to_owned()更慢这种很尴尬的局面,持续了一年多时间。
001 impl ToString for str { 002 #[inline] 003 fn to_string(&self) -> String { 004 String::from(self) 005 } 006 }
Rust语言支持impl specialization特性之后,就可以在标准库里为str类型添加一个更具体、更specific的ToString实现:
impl specialization (RFC 1210)设计稿自2015年6月推出第一版,至2016年2月,期间经过及其广泛而深入的高质量的公开的设计讨论,数易其稿,最终被正式批准采纳。2016年3月,该设计稿被部分实现。impl ToString for str已经成为现实。
foo?.bar()?(RFC 243)
Rust现有的错误处理机制(try! +Result)虽然还不错,但是使用有些繁琐,对链式调用不友好。try!不是语言内置特性,而只是标准库里定义的一个很简单的宏。长期以来,Rust开发者一直在寻找更好更简洁的错误处理机制,进行了许多探索和讨论。2014年9月RFC 243被提出,2015年10月经过重大设计改进,2016年2月被正式采纳,3月被初步实现,期间经过大量设计讨论。至今foo? .bar()?语法已经待在nightly版本里被内部测试三个月时间,如果不出意外估计不久就会进入stable版本跟大家正式见面。发布之前要做的其中一项工作将是把标准库里面的try!全面切换到?(已经有自动化工具untry可用)。这一特性毫无疑问将改变所有人编写代码的方式。
MIR(RFC 1211)
现在的Rust编译器,是从2010年开始开发的,是用2010年的Rust语言编写的。那个时候,Rust的0.1版本都还没出娘胎呢。此后的5年时间里,Rust语言在进化过程中不断地发生天翻地覆的变动。Rust编译器正是在这样动荡的环境下逐步迭代完成开发。我们认为,Rust编译器是由许多个不同版本的Rust语言写成的;甚至可以说,Rust编译器是由许多不同的编程语言写成的(因为每个版本的变化都非常大)。结果就是,在编译器源代码的角落里,难免存在一些陈旧代码和历史遗留问题,并在一定程度上阻碍了编译器的维护和升级。
MIR计划正是在这样的背景下诞生的。通过在编译器内部引入中层中间表示码(Mid-level Intermediate Representation,介于上层HIR和底层LLVM IR之间),对编译器代码进行大型重构、改造。这一基础性的改进,对编译器今后的维护、升级,具有深远的影响。许多新特性可在MIR的基础上更方便的实现。例如,增量编译、代码优化、更精确的类型检查等等(Non-zeroing drop, Non-lexical lifetimes),都依赖于MIR计划的实施。开发者甚至在计划借助MIR直接生成WebAssembly字节码并已着手行动(WebAssembly:JavaScript垄断地位的终结者)。
2015年7、8月份完成MIR的设计,此后很快进入长达十个月的编码实施阶段。任务量非常大,但进展有条不紊,Github忠实记录了一切。到2016年5月份,MIR代码生成工作基本完成,基于MIR的进一步优化和应用,尚在进行中。
alloctors (RFC 1398 1183)
2014年4月提出的RFC PR 39未获通过,同年9月的RFC PR 224也未获通过,直到2015年12月的RFC 1398终于有所斩获,在历经数十次修订后,于2016年4月修成正果,成为内存分配器标准接口的正式规范,为今后开发者自行实现各种类型的内存分配器扫清了障碍。其背后的动机是,系统默认提供的内存分配器(例如jemalloc)不可能适合所有应用场景,第三方内存分配器的引入是必要的。
从整个漫长过程我们看到设计的艰难、设计者的不懈努力,和坚持追求高质量设计的严肃态度。
于此同时,允许变更系统默认内存分配器的设计工作也被提上日程。RFC 1183在2015年7月推出设计稿,一个月后获得官方审核批准。8月份修改编译器,完成编码工作,完整地实现了此RFC。jemalloc不再是强制使用的内存分配器,程序员有机会选择使用其他内存分配器作为默认内存分配器。这一功能已被内部测试了将近一年,预计不久之后将会正式发布。
编译错误提示
编译错误提示更加简洁和人性化。
以前与现在的对比见图1和图2。
图1
图2
2016年5月初,此功能已经在nightly版本中实现。再经过一段时间的培育,确认其稳定工作之后,将会进入stable版本正式面世。(注,上面的附图是稍早的版本,后面又有所调整和改进。)
其他
增量编译(Incremental compilation):2015年8月至11月完成增量编译的设计文档(RFC 1298)。相关编码实现工作缓慢进展,部分依赖于MIR。
Macro 2.0:新一代宏2.0的规划和设计取得较大进展,nrc做了大量的前期研究工作,提出了设计稿RFC PR 1566。接下去的进展有待观察。
impl Trait:取得了许多探索和研究成果(1 2 3), RFC PR 1522得到了比较广泛的认同,很快会有下一步的动作。完成之后将改善“函数返回迭代器”的现状(struct扎堆)。
单指令多数据(SIMD): Rust对SIMD硬件加速支持取得初步进展,huonw做了大量前期工作,提出RFC 1199(2015年9月获得批准),提交基础实现和扩展库。属于实验性支持阶段,已在regex库中得到应用(性能提升十分明显)。
Rust周边开发工具升级
Cargo
cargo install:只需一个很简单的命令(cargo install xxx)就能编译安装可执行程序到本地.cargo/bin目录(此目录一般都在PATH环境变量内)。设计和实现均已完成。像clippy, rustfmt, cargo-edit等都能通过它安装,十分方便。
cargo workspace:让Rust项目的源代码目录结构和布局更加灵活。设计完成但尚未实现(在本文创作后期已经实现)。
cargo concurrently:允许多个Cargo实例并发执行。已经初步完成。
cargo namespacing:官方开发者始终持有主动排斥的不作为态度,因而一年来没有任何进展。跟Go语言泛型面临的悲催处境惊人的相似。
clippy
clippy得到了许多人发自内心的喜爱。它会分析审查你的源代码,并提出许多贴心的改进意见。它像良师益友,安静地坐在你身边,面对面指导你编写代码。经常使用它,有助于改善代码质量、提升开发者的编程经验和水平,无论新手老手都能从中受益。
Thanks rust-clippy. We do love the great project.
安装方法:cargo install clippy;使用方法:cargo clippy。
rustfmt
格式化Rust源代码的工具,已经基本成熟、稳定可用,许多官方库用它格式化代码。不过它存在的意义相比clippy弱暴了。rustfmt只是辅助修整一些皮面而已,对语意几乎一窍不通,我想不通某些哥们经常对这类工具推崇备至。安装方法:cargo install rustfmt;使用方法:cargo fmt或rustfmt。
rustup
Rust安装和更新工具,可以方便的切换stable和nightly版本,可以下载和使用各种平台交叉编译工具链。前身是Shell脚本,现在已经是纯Rust开发(rustup.rs)。开发者首选的Rust安装方式。这里还要顺便提及brson为方便各操作系统打包Rust所付出的努力。
rustbuild
专为Rust编译器和标准库定制的基于Cargo的编译工具,用于取代之前基于Makefile的编译系统(已经过于庞大和复杂难以维护)。已经做了大量前期工作。
Editors & IDEs
各主流代码编辑器/IDE都对Rust提供或多或少的某种程度的支持。语法高亮着色、代码自动完成等基本功能都已实现。RFC 1317(讨论)还进行了深入的思考和规划,未来Rust编译器和其他工具将主动对IDE提供更友好的支持(但是目前还没有看到具体的动作)。
Visual Studio Code (VSCode)+RustyCode
· Sublime Text 3+RustAutoComplete
· IntelliJ IDEA+intellij-rust
· Vim+rust.vim+YouCompleteMe
· Emacs+rust-mode
详情可参考Are we IDE yet,或参见Reddit网友讨论帖:Show me your programming environment。官方网站也有总结。
以上几乎所有编辑器&IDE及其插件的背后都离不开大功臣Racer:Rust“代码提示&自动完成”工具。
我现在感觉用VSCode v1.2比较顺手,比Atom v1.8反应快还更稳定。作为通用的代码编辑器,我原本对Atom寄予厚望,但现在来看VSCode有后来居上的趋势。
Rust第三方应用升级
Dropbox Magic-Pocket
2016年3月份,Dropbox公司脱离Amazon云存储平台的战略规划和实施细节浮出水面(低调做事之后高调发布,这风格我喜欢)。国际新闻报道标题是:“The Epic Story of Dropbox's Exodus From the Amazon Cloud Empire”(英文讨论)。国内新闻报道标题是:“Dropbox用Rust取代Go精简内存占用”(中文讨论)。Dropbox这一计划的核心是自己开发云存储系统,并逐步过渡。一开始是用Golang语言开发,后来上线后发现系统占用内存太厉害,于是改用Rust语言开发该系统的核心模块Magic Pocket。对外报道时,新系统已经上线一段时间,运行状况良好。
Redox OS
Redox OS是真撸啊。想搞另一个Linux出来。
用Rust语言开发的Redox操作系统,2015年9月一经面世就拥有图形用户界面,现已实现文件系统(ZFS WIP)、网络系统、多线程、文本编辑器等等。微内核设计,硬件驱动运行在用户空间。兼容大多数Linux系统调用和常见的Unix命令。
它可在Linux、Windows、OS X上编译,可在真实硬件上(Panasonic TOUGHBOOK CF-18、IBM Thinkpad T-420、ASUS eeePC 900)启动并运行GUI应用和Console应用。一个团队还在不断的开发完善Redox系统。开发活跃,进展很快,文档较全。
个人或小团队开发出来的绝大多数所谓的操作系统,都是习作、玩具。那些借助BootLoader启动起来并在屏幕上输出文字的所谓内核,除了作为新手入门练习之外没有任何应用价值。Redox显然不属于此类,Redox有更高的追求。Redox的作者也不是新手,而是资深的OS开发专家。
· Rust's Redox OS could show Linux a few new tricks
Redox OS固然牛逼,可它也不是一个人在战斗,用Rust开发的操作系统有好几个呢。
TiKV 分布式存储引擎
PingCAP公司推出的TiDB是开源的分布式数据库,参考Google F1/Spanner实现了水平伸缩,一致性的分布式事务,多副本同步复制等重要NewSQL特性。结合了RDBMS和NoSQL的优点,同时完全兼容MySQL。前期用Go语言实现了SQL Layer,后来(2015年底)考虑到“Go的GC和Runtime对底层存储非常不友好”,再加上对运行效率的极致要求,决定采用Rust语言开发TiDB的核心存储模块TiKV。2016年1月份至今,TiKV项目的开发十分活跃,至少有4位软件工程师全职参与开发。4月份刚刚开源。
Diesel ORM
作者Sean Griffin是Ruby Rails ActiveRecord的现任活跃维护者,是ORM领域的专家。他在2015年9月启动Diesel项目。来自ActiveRecord的优秀设计、经验和教训,有助于他设计实现这个全新的ORM项目。Diesel项目的设计充分发挥Rust类型安全的优势,还特别注重性能和可扩展性,文档也不错。是颇受关注、值得期待的项目。