[译] Rust 2018 已经发布……但它究竟是什么呢?

本文是与 Rust 团队(指文中“咱们”)合著。你也可在 Rust 博客上阅读他们的声明html

今天,Rust 2018 发布了它的第一个版本,在这个版本中,咱们专一于提升生产力,让 Rust 开发人员尽量高效。前端

时间线显示了不一样的进程,Rust 2018 和 Rust 2015,而且 beta 版上的功能指向另两个版本。包围时间线的是工具图标及 4 个领域:WebAssembly,嵌入式,网络和命令行界面(CLI)。一个红色的环包围了全部的东西(除了 Rust 2018 字样和 Developer Productivity 标签)

但除了这些,很难准确解释 Rust 2018 是什么。android

一些人认为它是一门语言的新版本,确实能够这么认为,但其实又不彻底是。我说“不彻底是”是由于若是这是语言新版本,它并不像其它语言的版本更新。ios

在大多数其它语言中,当新版本到来时任何新功能都会添加到此版本中。以前版本并不会获得功能更新。git

Rust 的版本则不一样。这是由于语言的演进方式不一样。几乎全部新功能都会 100% 兼容原有的 Rust。他们不须要重大的更新。这意味着不须要把他们限制在 Rust 2018 代码中。新版本的编译器会继续支持“Rust 2015 模式”,也就是你默认使用的模式。程序员

然而由于有时候要改进语言,你必须添加新的东西(像新语法)。而且这类新语法会在老的代码库下执行失败。github

async/await 举例来讲。Rust 最初并无 asyncawait 的概念。可是事实证实这些简单的语法实际上很是有用。它们让异步代码易于编写而且不会使代码变得笨重。web

为了让添加这个功能成为可能,咱们须要把 asyncawait 添加为关键字。但咱们必须当心不能使旧代码失效……代码可能会把 asyncawait 当作变量名使用。shell

因此咱们把添加关键字做为 Rust 2018 的一部分。虽然这个功能自己还没实现,但如今关键字已经被保留。接下来三年开发(例如添加关键字)所需的全部重大变化都是在 Rust 1.3.1 中一次性完成的。npm

时间线显示 Rust 2015 链接到 Rust 2018 发布的 1.31 版本

虽然 Rust 2018 有重大的变化,但这并不意味着你的代码会执行失败。你的代码会继续编译甚至会把 asyncawait 当作变量名。除非你告诉它,否则编译器会假设你想让它当前以相同的方式来编译代码。

不过一旦你想使用这些新的重大功能,你能够设置成 Rust 2018 模式。你仅须要执行 cargo fix,它会告诉你若是你须要更新你的代码来使用新功能。它几乎能够自动地处理更改。而后你能够添加 edition=2018 到你的 Cargo.toml 来设置和使用新功能。

Cargo.toml 中的 edition 说明符不适用于你的整个项目……它不适用于你的依赖。它只限于一个包。这意味着你将可以拥有相互依赖的 Rust 2015 和 Rust 2018 包。

所以,即便 Rust 2018 已经出现,它大体看起来与 Rust 2015 相同。大多数变化都将同时出如今 Rust 2018 和 Rust 2015 中。只有少数须要重大变化的功能不会同步支持。

Rust 2018 不只仅是对核心语言的改变。事实上,远非如此。

Rust 2018 致力于提升 Rust 开发人员的工做效率。许多生产力的提升来自核心语言之外的东西……好比工具。他们还专一于特定用例,并弄清楚 Rust 如何成为这些用例最有效的语言。

所以,你能够将 Rust 2018 视为 Cargo.toml 中的说明符,你可使用它来启用少数须要重大更改的功能……

时间线的箭头指向几个 Rust 2018 的功能,这些功能在 Rust 2015 中不会经过。

或者你能够把它想象成一个时刻,在许多状况下,Rust 成为你可使用的最有效的语言之一 —— 每当你须要性能,轻巧的实现或高可靠性时。

在咱们看来,这是第二点。让咱们先来看看核心语言以外的全部事情。而后咱们能够深刻研究核心语言自己。

针对特定用例的 Rust

抽象描述的编程语言自身并不能高效,但在一些用例中它会颇有效。所以,团队知道咱们不只须要将 Rust 做为一种语言或 Rust 工具更好,咱们还须要在特定领域中使 Rust 更容易使用。

在某些状况下,这意味着要为一个全新的生态系统建立一套全新的工具。

在其余状况下,它意味着打磨已经存在于生态系统中的内容并将文档作好,以便保持生态系统的成长和正常运做。

Rust 团队成立了专一于四个领域的工做组:

  • WebAssembly
  • 嵌入式应用
  • 网络
  • 命令行工具

WebAssembly

对于 WebAssembly,工做组须要建立一整套新工具。

就在去年,WebAssembly 让编译像 Rust 这样的语言在 Web 上运行成为可能。从那时起,Rust 迅速成为与现有 Web 应用程序集成的最佳语言。

Rust logo 和 JS logo 之间有一颗心

Rust 很是适合 Web 开发,缘由有两个:

  1. Cargo 包生态系统的工做方式与大多数 Web 应用程序开发人员习惯的方式相同。你将一堆小模块组合在一块儿构成一个更大的应用程序。这意味着在你须要的地方能够很容易地使用 Rust。
  2. Rust 实现简单而且不须要特定的运行环境。这意味着你无需嵌入大量代码。若是你须要一个很小的模块进行大量繁重的计算工做,你能够引入几行 Rust 来加快运行速度。

使用 web-sysjs-sys crates,很容易在 Rust 代码调用相似于 fetchappendChild 的 Web API 。而且 wasm-bindgen 能够轻松支持 WebAssembly 原生不支持的高级数据类型。

一旦编写了 Rust WebAssembly 模块,就可使用各类工具将其嵌入到 Web 应用程序的其他部分中。你可使用 wasm-pack 自动运行这些工具,并根据须要将新模块推送到 npm。

Check out the Rust and WebAssembly book to try it yourself. 建议你查看 Rust 嵌入式书籍并亲自尝试一下。

下一步呢?

如今 Rust 2018 已经完成,工做组正在肯定下一步要作什么。他们将与社区合做,肯定下一个重点领域。

嵌入式

对于嵌入式的开发,工做组须要使现存的功能稳定。

理论上,Rust 对于嵌入式开发来讲已是很是出色的语言了。它为嵌入式开发人员提供了他们很是缺少的现代工具,以及很是方便的高级语言功能。而且不须要耗费资源。所以,Rust 彷佛很是适合嵌入式开发。

然而,实际上它并很差使用。必备的功能处在不稳定阶段。另外,为了在嵌入式设备上使用,须要对标准库作一些改变。这意味着人们必须编译他们本身版本的 Rust 核心包(用来给每一个 Rust 应用提供 Rust 的基本构建模块 —— 内部模块和原始值)

左边:一我的骑在猛然弓背跃起的微处理器芯片上,说“Whoa, Rusty!”。右边,一我的骑在一个驯服的微处理器芯片上说“很好 Rusty,乖乖地保持稳定”

总之,这两件事意味着开发人员必须依赖于 Rust 的每日构建版本。而且因为没有针对微控制器的自动化测试,每日构建版在这些目标上会常常出错。

为了解决这个问题,工做组须要保证稳定版本有那些必要的功能。咱们也必须向持续集成系统为微控制器目标添加测试。这意味着向桌面端组件添加功能不会破坏嵌入式组件的东西。

有了这些变化,Rust 的嵌入式开发将从有风险的前沿迈向高效。

建议你查看 Rust 嵌入式书籍并亲自尝试一下。

下一步呢?

随着近年来的推动,Rust 已经很好地支持了 ARM Cortex-M 架构的芯片处理器核心,这些核心用于大量设备。然而,还有不少被用于嵌入式设备的架构没有获得很好的支持,而且没有被很好的支持。Rust 须要被扩展来给这些架构以一样水平的支持。

网络

对于网络来讲,工做组须要构建核心抽象概念到语言中 —— async/await。这样,开发者能够在异步代码中使用符合语言习惯的 Rust。

对于网络任务来讲,你必需要等待。例如,你可能须要等待请求的响应。若是你的代码是同步的,那意味着 CPU 核心正在执行的任务会中止并且不能会作其它的任何事情,直到请求进来。但若是你的代码是异步的,那么等待响应的函数会在 CPU 核心执行其它函数时挂起。

使用 Rust 2015 使异步编程成为可能。而且还有不少优势。从大的方面讲,像服务端应用之类的服务,你的异步代码使得每一个服务器能够处理更多的链接。从小的方面讲,对于那些运行在小型单核 CPU 上的嵌入式应用,异步使你更好的利用单线程。

但这些优势也带来了一个大的缺点 —— 你不能为那些代码使用借用检查器,而且你将必须书写符合语法习惯的(和别的使人迷惑的) Rust。这就是 async/await 出现的理由。它给予编译器须要的信息,这些信息用来在异步函数之间调用 borrow check。

async/await 关键字在 1.31 被引入,虽然它们目前的实现还不能向下兼容。但大部分工做都已完成,而且你能够期待这个功能在将要到来的一个版本中可用。

下一步呢?

除了为网络应用程序实现高效的低层次开发以外,Rust 还能够在更高层次上实现更高效的开发。

许多服务器须要去处理相同类型的任务。它们须要解析 URL 或者处理 HTTP 任务。若是把它们变成组件通用抽象类型,而且在包之间分享 —— 那么就能够轻松地把它们组合在一块儿来组成各类各样的服务和框架。

为了驱动组件开发过程,Tide 组件 为这些组件提供了测试平台,并最终展现这些组件。

命令行工具

对于命令行工具,工做组须要给更小,更低级的库引入高级的抽象,而且打磨已有的工具。

对于一些 CLI 脚本,你真的很想使用 bash。例如,若是你仅仅须要唤出其余 shell 工具并在它们之间使用管道传输数据,bash 是最好的选择。

不过 Rust 也很是适于其余的命令行工具。好比你正在构建一个复杂的工具像 ripgrep 或构建一个处在现有的库功能之上的 CLI。

Rust 不须要运行时而且容许你编译进单独的静态二进制文件,这样利于分发。而且你能获得其它语言向 C 或 C++ 得不到的高级抽象,这些特性已经让 Rust CLI 开发者更高效。

工做组须要作些什么来改善它?是更高级的抽象。

有了这些高级抽象,组合成熟的 CLI 会快速并轻松。

这些抽象中的其中一个是 human panic 库。没有这个库,若是你的 CLI 代码报错,他可能会输出整个错误栈。但这对于你的终端用户没有帮助。你能够添加自定义错误处理,不过那须要额外工做。

若是你用了 human panic,那么输出会自动地转到错误转储文件。而用户会看到有帮助的消息建议他们报告这个问题并上传错误转储文件。

命令行使用 hunam-panic 友好地输出

工做组也让 CLI 开发更容易入手。例如,confy 库会自动处理一些新 CLI 工具的安装事项。它只会问你两件事:

  • 你的应用叫什么名字?
  • 你想暴露那些配置选项(你定义的结构能够被被序列化和反序列化)?

从这个问题出发,confy 会帮你处理剩余的事情。

下一步呢?

工做组会抽象出不少不一样的任务,这些任务能够在不一样的 CLI 中共用。可是还有更多能够抽象的。工做组会制做更多相似的高级库,并随着时间推移解决更多的 Pager cut bug

Rust 工具

工具图标

当你体验一门语言时,你须要经过工具来体验。从你使用的编辑器开始,它贯穿于开发和维护的各个阶段。

这意味着高效的语言依赖于高效工具。

这里有一些工具(包含一些现有 Rust 工具的改进)会做为 Rust 2018 的一部分被引进。

IDE 支持

固然,高效的关键是将代码从头脑中迅速传递到屏幕上。IDE 的支持严重影响这一点。为了支持 IDE,咱们须要一些工具来告诉 IDE Rust 代码的实际含义 —— 例如,告诉 IDE 什么字符串对代码完成有意义。

在 Rust 2018 推送中,社区聚焦于 IDE 须要的功能。随着 Rust Language Server 和 IntelliJ Rust 的发展,如今许多 IDE 已经可以对 Rust 有良好的支持。

更快的编译

更快的编译意味着更高效。因此咱们使编译器更快。

之前,当你想编译 Rust 包,编译器会重复编译每一个包里的单独文件。可是如今,使用增量编译让编译器变得智能而且只会重复编译已经改变的部分。这和其它的优化一块儿使得 Rust 编译器更加迅速。

rustfmt

高效也意味着不须要天天解决风格问题(再不须要去争论代码风格规则)。

rustfmt 工具经过使用(已与社区达成共识的)默认代码风格自动格式化你的代码。使用 rustfmt 保证你全部的 Rust 代码符合相同的风格。就像 C++ 使用 clang format 和 JavaScript 使用 Prettier 那样。

Clippy

有时候你的身旁能有个经验丰富的顾问会很是好……给你提出一些代码的最佳实践。这就是 Clippy 作的东西 —— 它会审查你的代码实现并告诉你怎样让代码更符合语言习惯。

rustfix

可是若是你在维护一个使用过期的语法的老旧代码库,那么只是获取提示并本身来纠正代码可能会很乏味。你只是但愿有人进入代码库来更正这些问题。。

对于这些状况,rustfix 会自动化该过程。它会同时应用来自 Clippy 等工具的 lint 并更新旧的代码来匹配 Rust 2018 语法。

Rust 自己的变化

生态系统的这些变化已经带来大量的生产力,可是一些生产力问题只能经过语言自己的变化来解决。

就像我在简介中说过的,大部分语言层面的变动彻底兼容已有的 Rust 代码。这些变动都是 Rust 2018 变动的一部分。不过由于它们不会破坏原有任何代码,它们也能够在任何 Rust 代码中运行……甚至是没有使用 Rust 2018 的代码。

让咱们看一些被加到所有版本的重大语言功能。而后咱们能够看一下 Rust 2018 特点功能的小清单。

适用所有版本的语言新功能

这里有一个重大新语言功能的小型示例,它(或将)被包含在全部的语言版本中

更精确的 borrow checking(例如:非词法有效期)

Rust 的一大卖点就是借用检查器。借用检查器帮助你确保你的代码是内存安全的。但对于 Rust 开发新手来讲它也一个痛点。

部分缘由是须要学习新的概念。但还有一个大的缘由……借用检查器有时候会拒绝那些看起来应该工做的代码,甚至对于那些概念有足够理解的人来讲,他们也会遇到这种状况。

借用检查器告诉程序员由于变量已经被借走了,因此不能去借这个变量

这是由于一次借用的有效期是到它的做用域结束为止 —— 例如,变量的有效期到函数结束为止。

这意味着尽管变量值的有效期已经结束而且不能访问,别的变量仍然被拒绝直到函数结束。

为了解决这个问题,咱们使得借用检查器更加智能。如今它能够看到实际正在使用的变量。若是它的有效期结束,那么它不会阻塞其余使用数据的借用。

借用检查器说:啊,我如今看获得了

不过当前这个特性只支持在 Rust 2018 中使用,而在不远的未来,全部 Rust 版本都将可使用。以后我很会写更多关于这部分的内容。

稳定的 Rust 过程宏

Rust 中的宏已经出如今 Rust 1.0 以前。可是在 Rust 2018 中,咱们对它作了一些重大的改进,好比引入过程宏。

使用过程宏,有点像你能够添加本身的语法到 Rust。

Rust 2018 带来了两种过程宏:

类函数宏

类函数宏容许你拥有看起来像常规函数调用的东西,但这些东西其实是在编译期间运行的。他们接受一些代码并输出不一样的代码,而后编译器将这些代码插入到二进制文件中。

他们已经存在了一段时间,但你能用它们作的事情有限。你的宏只能获取输入代码并在其上运行匹配语句。它无权查看输入代码中的全部令牌。

可是使用过程宏,你能够得到与解析器相同的输入 —— 令牌流。这意味着能够建立更强大的类函数宏。

类属性宏

若是你熟悉 JavaScript 等语言中的装饰器,属性宏和它很是类似。它们容许你在 Rust 中注解应该预处理并转换为其余内容的代码。

derive 宏就是作这种事情的东西。当你把 derive 放到一个结构上时,编译器会把这个结构输入(在它被解析为一个令牌列表以后)并处理它。具体来讲,它将从特征中添加函数的基本实现。

更多易于理解的借用匹配

这种变化很是直观。

之前,若是你想借一些资源并尝试匹配它,你不得不添加一些奇怪的语法:

旧版本的代码使用 &Some(ref s),新版本使用 Some(s)

但如今,你再也不须要写 &Some(ref s) 了。你能够只写 Some(s),Rust 能清楚地找到(它们之间的)差别。

Rust 2018 特有的新功能

Rust 2018 的最小部分是它特有的功能。如下是 Rust 2018 版本解锁的少数几项更改。

关键字

Rust 2018 中添加了一些关键字。

  • try 关键字
  • async/await 关键字

这些功能还没有彻底实现,但对应的关键字正在向 Rust 1.31 添加中。这意味着在未来,当咱们实现了这些关键字背后的功能时,也不须要引入新的关键字(引入关键字将会是一个有破坏性的变动。

模块系统

开发人员学习 Rust 的一个痛点是模块系统。咱们能够来看看为何(是这样)。(咱们)很难去推断 Rust 会使用哪一个模块。

为了解决这个问题,咱们对 Rust 中路径的工做方式进行了一些更改。

例如,若是你导入了一个包,则能够在顶级路径中使用它。可是,若是你将任何代码移动到子模块,那么它将再也不起做用。

// 顶层模块
extern crate serde;

// 这样在顶层模块中能够工做
impl serde::Serialize for MyType { ... }

mod foo {
  // 但它在子模块中**不能**工做
  impl serde::Serialize for OtherType { ... }
}
复制代码

另外一个例子是前缀 ::,它被用来指代当前包的根目录或一个外部包,这(两种状况)可能很难被(使用者)区分。

咱们已经明确了这一点。如今,若是要引用包的根路径,则使用前缀 crate::。这只是咱们所作的 path clarity 改进之一。

若是你有现存的 Rust 代码而且但愿它使用 Rust 2018,那么你极可能须要为这些新的模块路径更新它。但那并不意味着你须要手动更新代码。在将版本说明符添加到 Cargo.toml 以前运行 cargo fix ,而且运行 rustfix 会为你进行全部更改。

了解更多

Rust 2018 版本指南 中了解这个版本的更多内容

关于 Lin Clark

Lin 是 Mozilla Developer Relations 团队的工程师。她了解 JavaScript、WebAssembly、Rust 和 Servo,还能够绘制代码漫画(code cartoons)。

Lin Clark 的更多文章…

关于 Rust 团队

若是发现译文存在错误或其余须要改进的地方,欢迎到 掘金翻译计划 对译文进行修改并 PR,也可得到相应奖励积分。文章开头的 本文永久连接 即为本文在 GitHub 上的 MarkDown 连接。


掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 AndroidiOS前端后端区块链产品设计人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划官方微博知乎专栏

相关文章
相关标签/搜索