Rust 2018出来了...但这是什么?

本文是由Lin Clarke与Rust开发团队合作编写的(本文中的“我们”)。 您还可以在Rust官方博客上阅读该帖子

Rust 2018的第一版于2018年12月6日发布。在此版本中,我们专注于生产力,以便Rust开发人员开始尽可能高效地工作。


时间轴显示了从beta到Rust 2018和Rust 2015的过渡。它由工具图标和四个区域包围:WebAssembly,嵌入式,联网和CLI。 红色圆圈-开发人员的生产力-涵盖了Rust 2015以外的所有内容

但总的来说,要解释Rust 2018是什么并不容易。

有些人将其作为语言的新版本来呈现……类似的东西,但并非如此。 我说“不是真的”,因为这里的“新版本”并不意味着其他语言的新版本。

在大多数其他语言中,所有新功能都会添加新版本。 以前的版本未更新。

Rust系统的运行方式有所不同。 这是由于语言的发展。 几乎所有新功能都与Rust 100%兼容。 它们不需要任何更改。 这意味着没有理由将它们限制为Rust 2018代码,默认情况下,较新版本的编译器将继续支持“ Rust 2015模式”。

但是有时语言的发展需要创新,例如新的语法。 而且这种新语法可能会破坏现有的代码库。

例如, async/await功能。 最初,Rust中没有这样的概念。 但是事实证明,这些原语确实有用,它们简化了异步代码的编写。

对于此功能,必须添加关键字asyncawait 。 但是您应该注意不要破坏将asyncawait用作变量名称的旧代码。

因此,我们在Rust 2018中添加了关键字。尽管该功能尚未发布,但现在保留了关键字。 在接下来的三年中,所有不兼容的更改(例如,添加新关键字)都是在Rust 1.31中一次进行的。



尽管Rust 2018中存在不兼容的更改,但这并不意味着您的代码将被破坏。 即使使用asyncawait变量,代码也将编译。 默认情况下,编译器将像以前一样工作。

但是,如果要使用新功能之一,则可以选择Rust 2018的新编译模式。cargo cargo fix命令将告诉您是否需要更新代码以使用新功能并自动进行更改。 然后,如果您同意使用新功能,则可以在Cargo.toml中添加edition=2018

Cargo.toml中的此版本说明符不适用于整个项目,也不适用于您的依赖项。 它仅限于一个特定的机架。 也就是说,您可以同时使用Rust 2015和Rust 2018包装箱。



因此,即使使用Rust 2018,所有内容看起来都与Rust 2015相同。大多数更改是在Rust 2018和Rust 2015中同时实现的。只有很少的功能需要不兼容的更改。



Rust 2018不仅是主要语言的变化。 不仅如此。

首先,Rust 2018是提高Rust开发人员生产力的动力,这在很大程度上要归功于该语言之外的工具,以及通过开发特定应用程序以及了解如何使Rust成为这些情况下最有效的编程语言。

因此,您可以将Rust 2018表示为Cargo.toml中的说明符,用于包含一些需要不兼容更改的功能...



或者,您可以想象在Rust成为许多应用程序中最有效的语言之一的时候-当您需要性能,资源的有效使用或高可靠性时。



我们更喜欢该定义的第二个版本。 因此,让我们看一下在语言之外进行的所有改进,然后再深入探讨语言本身。

防锈特定应用


编程语言本身不能抽象地有效。 在特定应用中有效。 因此,我们了解到,不仅需要将Rust改进为一种语言或工具。 还必须简化在某些区域中Rust的使用。



在某些情况下,这意味着为全新的生态系统创建一组全新的工具。 在其他情况下,请完善现有功能和良好的文档资料,以更轻松地启动和运行工作系统。

Rust开发团队已经在四个领域组成了工作组:

  • 网络组装
  • 嵌入式应用
  • 网络任务
  • 命令行工具

网络组装


WebAssembly必须创建一套全新的工具。

仅在去年,WebAssembly才使编译Rust等语言以在Internet上运行成为可能。 从那时起,Rust已迅速成为与现有Web应用程序集成的最佳语言。



Rust非常适合于Web开发,原因有两个:

  1. 货运事故生态系统的工作方式与大多数Web应用程序开发人员惯用的方式相同。 结合一堆小模块以形成更大的应用程序。 这意味着Rust可以在需要的地方轻松使用。
  2. Rust的资源不足,不需要运行时。 您不需要很多代码。 如果您有一个微型模块可以执行大量的硬计算工作,请实施一些Rust线以加快速度。

使用Rust代码中的web-sys和js-sys ,可以轻松调用诸如fetchappendChild类的Web API。 wasm-bindgen使得支持WebAssembly本身不支持的高级数据类型变得容易。

编写Rust WebAssembly模块后,有一些工具可以轻松地将其连接到Web应用程序的其余部分。 您可以使用wasm-pack自动启动这些工具,并根据需要在npm中运行该模块。

有关更多信息,请参见《 Rust and WebAssembly一书

接下来是什么?


在Rust 2018发布之后,开发人员计划与社区讨论进一步发展的方向。

嵌入式应用


对于嵌入式开发,有必要提高现有功能的稳定性。

从理论上讲,Rust一直是嵌入式应用程序的好语言。 这是一个现代的工具包,开发人员非常缺乏,它是非常方便的高级语言功能。 所有这些都不会给CPU和内存带来不必要的负担。 因此,Rust非常适合嵌入式。

但实际上,结果却有所不同。 稳定的频道缺少必要的功能。 此外,要在嵌入式设备上使用,有必要更改标准库。 这意味着人们必须编译自己的版本的Rust核心板条箱(每个Rust应用程序中使用的板条箱提供基本的Rust构造块-内置函数和基元)。



结果,开发人员依赖于Rust的实验版本。 而且在没有自动测试的情况下,实验组件通常无法在微控制器上工作。

为了解决此问题,开发人员试图将所有必要的功能转移到稳定的通道,并向微控制器的CI系统添加测试。 这意味着更改桌面组件不会破坏内置版本。

有了这样的变化,Rust上嵌入式系统的开发正从高级实验领域转移到正常效率领域。

有关更多信息,请参见《 Rust for Embedded Systems》

接下来是什么?


今年,Rust为流行的ARM Cortex-M系列提供了很好的支持。 但是,许多架构尚未得到很好的支持。 需要扩展Rust以便为其他架构提供类似的支持。

网络任务


为了在网络上工作,有必要在该语言中嵌入一个关键的抽象: async/await 。 因此,即使在异步代码中,开发人员也可以使用标准的Rust习惯用语。

在网络任务中,您通常必须等待。 例如,对请求的响应。 如果代码是同步的,则工作将停止:在代码到其上执行的处理器的核心,在请求到达之前,无法执行任何操作。 但是在异步代码中,此类功能可以置于待机模式,而CPU内核将完成其余工作。

在Rust 2015中也可以进行异步编程,并且这样做有很多优点。 在高性能应用程序中,服务器应用程序将处理与每个服务器的更多连接。 微型单线程CPU上的嵌入式应用程序优化了单线程的使用。

但是,这些优点伴随着一个主要缺点:对于这样的代码,借用验证不起作用,并且您必须使用非标准(且有些混淆)的Rust习惯用法。 这是async/await的好处。 这为编译器提供了必要的信息,以测试异步函数调用的借用。

async/await关键字在版本1.31 async/await实现,尽管该实现当前不支持它们。 大多数工作已经完成,并且该功能应在下一版本中可用。

接下来是什么?


除了有效的低级开发,Rust可以在更高级别上提供更有效的网络应用程序开发。

许多服务器执行例行任务:解析URL或使用HTTP。 如果将它们变成组件(作为包装箱共享的通用抽象),则将它们彼此轻松连接,形成各种服务器和框架配置将很容易。

已经创建了一个实验性的Tide框架来开发和测试组件。

命令行工具


对于命令行工具,有必要将小型的低级库组合成高级抽象并完善一些现有工具。

对于某些脚本,bash是理想的选择。 例如,仅调用其他Shell工具并在它们之间传递数据。

但是Rust是许多其他工具的绝佳选择。 例如,如果您在现有库的功能之上创建诸如ripgrep之类的复杂工具或CLI工具。

Rust不需要运行时,并且可以编译为单个静态二进制文件,从而简化了程序的分发。 您会获得高级语言的抽象,而这些语言不是C和C ++等其他语言的。

还有什么可以改善Rust的? 当然,抽象水平更高。

通过更高级别的抽象,可以快速轻松地组装现成的CLI。

人类恐慌库就是这种抽象的一个例子。 如果没有此类库,则在发生故障的情况下,CLI代码可能会返回所有回溯。 但这对用户来说不是很有趣。 您可以添加自定义错误处理,但这很困难。

使用紧急恐慌库,输出将自动转到错误转储文件。 用户将看到一条提示性消​​息,报告问题并下载转储文件。



开始开发CLI工具也变得更加容易。 例如, confy库自动执行其配置。 他只问两件事:

  • 该应用程序的名称是什么?
  • 您要提供哪些配置参数(定义为可以序列化和反序列化的结构)?

Confy将自行决定其他所有事项。

接下来是什么?


我们为CLI提取了许多任务。 但是还有其他要抽象的东西。 我们将发布更多这样的高级库。

防锈工具




使用任何语言编写时,都可以使用其工具:从编辑器开始,然后在开发和支持的所有阶段继续使用其他工具。

这意味着有效的语言取决于有效的工具。

这是Rust 2018中的一些新工具(并对现有工具进行了改进)。

IDE支持


当然,性能取决于将代码从开发人员的思想快速流畅地转移到计算机屏幕上的情况。 这是IDE支持至关重要的地方。 为此,我们需要可以“解释” IDE到Rust代码含义的工具:例如,建议有意义的选项来自动完成字符串。

在Rust 2018中,社区专注于IDE所需的功能。 随着Rust语言服务器和IntelliJ Rust的出现,许多IDE现在完全支持Rust。

更快的编译


改善编译器性能意味着加快速度。 这就是我们所做的。

以前,当您编译Rust板条箱时,编译器会重新编译板条箱中的每个文件。 现在实现了增量编译:它仅编译那些已更改的部分。 连同其他优化,这使Rust编译器更快。

生锈


效率还要求我们永远不要争论代码格式化规则或手动修复其他人的样式。

rustfmt工具可以帮助您:它将根据默认样式( 社区已达成共识 )自动将代码重新格式化。 Rustfmt确保所有Rust代码都匹配相同的样式,例如C ++的clang格式或JavaScript的Prettier。

短裙


有时,最好在附近有经验丰富的顾问就编写代码的最佳做法提供建议。 Clippy就是这样做的:它在查看代码时检查代码并建议标准习语。

生锈的


但是,如果您的旧代码库包含过时的习惯用法,那么独立验证和更正代码可能会很累人。 您只希望有人对整个代码库进行更正。

在这些情况下,rustfix使过程自动化。 它同时应用来自Clippy等工具的规则,并根据Rust 2018的习惯用法更新旧代码。

对Rust本身的更改


生态系统的变化大大提高了编程效率。 但是某些问题只能通过更改语言本身来解决。



正如我们在简介中所述,大多数语言更改都与现有的Rust代码完全兼容。 所有这些更改都是Rust 2018的一部分。但是,由于它们不会破坏任何内容,因此它们可以在任何Rust代码中使用,甚至可以在旧代码中使用。

让我们看一下所有版本中添加的重要功能。 然后查看Rust 2018的功能的简短列表。

所有版本的新功能


这是该语言所有版本中(或将要使用)的新功能的一个小示例。

更准确的借用验证


Rust的一大优势是其借用验证。 它确保代码是内存安全的。 但这对于Rust的新手来说也是一个相当复杂的功能。

部分困难在于学习新概念。 但是还有另一部分...测试借用有时会拒绝从完全了解内存安全性概念的程序员的角度来看似乎有效的代码。


您不能借用变量,因为它已被借用

之所以会发生这种情况,是因为借用生存期应该扩展到其字段的末尾,例如,扩展到变量所位于的函数的末尾。

这意味着即使变量完成了使用该值并且不再尝试访问,其他变量仍将被拒绝访问该值,直到函数结束。

为了纠正这种情况,我们使支票更加智能。 现在,她可以看到变量何时实际使用完该值。 之后,它不会阻止数据的使用。



尽管此功能仅在Rust 2018中可用,但在不久的将来,该功能将添加到所有其他版本中。 不久我们将在这个主题上写更多。

稳定Rust中的程序宏


Rust在Rust 1.0之前具有宏。 但是在Rust 2018中,进行了重大改进,例如出现了程序宏。 它们允许您将自己的语法添加到Rust。

Rust 2018提供了两种类型的程序宏:

功能宏


类似函数的宏允许您创建看起来像普通函数调用的对象,但实际上是在编译时执行的。 他们采用一个代码并给出另一个代码,然后编译器将其插入二进制文件中。

它们曾经存在过,但数量有限。 宏只能执行match语句。 他无权查看传入代码中的所有令牌。

但是,使用过程宏,您将获得与解析器相同的输入:相同的令牌流。 这意味着您可以创建功能更强大的宏。

类似于属性的宏


如果您熟悉JavaScript之类的语言中的装饰器,则属性宏非常相似。 它们使您可以注释应预处理的Rust代码片段,然后将其转换为其他内容。

derive宏就是这样做的。 将其放在结构上时,编译器将采用该结构(在将其解析为标记列表之后)并对其进行处理。 特别是,它从特性中添加了功能的基本实现。

比较中更符合人体工程学的设计


有一个简单的变化。

以前,如果您想借用某些东西并尝试匹配,则必须添加一些奇怪的语法:



现在,我们只写Some(s)而不是&Some(ref s) Some(s)

Rust 2018的新功能


Rust 2018的最小部分是特定于此版本的功能。 这是Rust 2018中的一小部分更改。

关键词


Rust 2018添加了一些关键字:

  • try
  • async/await

这些功能尚未完全实现,但是在Rust 1.31中添加了关键字。 因此,将来在实现这些功能时,我们将不必引入新的关键字(这将成为不兼容的更改)。

模块化系统


对于Rust的新手来说,最大的痛苦就是模块化系统。很清楚为什么。很难理解Rust为什么选择特定的模块。为了解决这个问题,我们对路径机制进行了一些更改。

例如,如果您导入了机架,则可以在顶层路径中使用它。但是,如果将任何代码移至子模块,它将不再起作用。

 // top level module extern crate serde; // this works fine at the top level impl serde::Serialize for MyType { ... } mod foo { // but it does *not* work in a sub-module impl serde::Serialize for OtherType { ... } } 

另一个示例是prefix ::,它用于包装箱的根和外部包装箱。很难理解摆在我们面前的是什么。

我们使它更加明确。现在,如果要引用根板条箱,请使用前缀crate::这只是为了清楚起见而进行的改进之一。

如果您希望当前代码使用Rust 2018的功能,则很可能需要更新代码以考虑新路径。但是没有必要手动执行此操作。在将版本说明符添加到Cargo.toml之前,只需运行它cargo fixrustfix进行必要的更改。

附加信息


《 Rust 2018指南》中包含有关该语言新版本的所有信息

Source: https://habr.com/ru/post/zh-CN432564/


All Articles