Rust 1.30 发布

2018 年 10 月 25 日 · Rust 核心团队

Rust 团队很高兴地宣布 Rust 的新版本 1.30.0。Rust 是一种专注于安全性、速度和并发性的系统编程语言。

如果您通过 rustup 安装了以前版本的 Rust,则获取 Rust 1.30.0 非常容易,只需

$ rustup update stable

如果您还没有安装,您可以从我们网站上的相应页面获取rustup,并查看 GitHub 上1.30.0 的详细发行说明

1.30.0 稳定版的新特性

Rust 1.30 是一个令人兴奋的版本,包含许多新特性。在周一,请期待另一篇博客文章,邀请您查看 Rust 1.31 的 beta 版本;Rust 1.31 将是“Rust 2018”的第一个版本。有关此概念的更多信息,请参阅我们之前的文章“什么是 Rust 2018”

过程宏

早在Rust 1.15中,我们就宣布了定义“自定义派生”的能力。例如,使用serde_derive,您可以

#[derive(Serialize, Deserialize, Debug)]
struct Pet {
    name: String,
}

并使用 serde_jsonPet 转换为 JSON 并从中转换,因为 serde_derive 在过程宏中定义了 SerializeDeserialize

Rust 1.30 通过添加定义另外两种高级宏的能力来扩展此功能,“类似属性的过程宏”和“类似函数的过程宏”。

类似属性的宏与自定义派生宏类似,但它们不仅为 #[derive] 属性生成代码,还允许您创建自己的新自定义属性。它们也更加灵活:派生仅适用于结构体和枚举,但属性可以应用于其他位置,例如函数。作为使用类似属性的宏的示例,当使用 Web 应用程序框架时,您可能会有如下代码:

#[route(GET, "/")]
fn index() {

#[route] 属性将由框架本身定义,作为过程宏。其签名如下:

#[proc_macro_attribute]
pub fn route(attr: TokenStream, item: TokenStream) -> TokenStream {

在这里,我们有两个输入 TokenStreams:第一个用于属性本身的内容,即 GET, "/" 的内容。第二个是附加属性的事物的主体,在本例中为 fn index() {} 以及函数的其余部分。

类似函数的宏定义看起来像函数调用的宏。例如,gnome-class crate 有一个过程宏,用于在 Rust 中定义 GObject 类:

gobject_gen!(
    class MyClass: GObject {
        foo: Cell<i32>,
        bar: RefCell<String>,
    }

    impl MyClass {
        virtual fn my_virtual_method(&self, x: i32) {
            // ... do something with x ...
        }
    }
)

这看起来像一个将一堆代码作为参数的函数。此宏将按如下方式定义:

#[proc_macro]
pub fn gobject_gen(input: TokenStream) -> TokenStream {

这类似于派生宏的签名:我们获取括号内的 token,并返回我们要生成的代码。

use 和宏

您现在可以使用 use 关键字将宏引入作用域。例如,要使用 serde-jsonjson 宏,您过去需要编写:

#[macro_use]
extern crate serde_json;

let john = json!({
    "name": "John Doe",
    "age": 43,
    "phones": [
        "+44 1234567",
        "+44 2345678"
    ]
});

但是现在,您可以编写:

extern crate serde_json;

use serde_json::json;

let john = json!({
    "name": "John Doe",
    "age": 43,
    "phones": [
        "+44 1234567",
        "+44 2345678"
    ]
});

这使宏与其他项更加一致,并消除了对 macro_use 注释的需求。

最后,proc_macro crate 已变为稳定版,这为您提供了编写此类宏所需的 API。它还显著改进了错误处理的 API,并且像 synquote 这样的 crate 已经在使用它们。例如,之前

#[derive(Serialize)]
struct Demo {
    ok: String,
    bad: std::thread::Thread,
}

会给出此错误:

error[E0277]: the trait bound `std::thread::Thread: _IMPL_SERIALIZE_FOR_Demo::_serde::Serialize` is not satisfied
 --> src/main.rs:3:10
  |
3 | #[derive(Serialize)]
  |          ^^^^^^^^^ the trait `_IMPL_SERIALIZE_FOR_Demo::_serde::Serialize` is not implemented for `std::thread::Thread`

现在它将给出此错误:

error[E0277]: the trait bound `std::thread::Thread: serde::Serialize` is not satisfied
 --> src/main.rs:7:5
  |
7 |     bad: std::thread::Thread,
  |     ^^^ the trait `serde::Serialize` is not implemented for `std::thread::Thread`

模块系统改进

模块系统长期以来一直是 Rust 新手的一个痛点;它的一些规则在实践中感觉很尴尬。这些更改是我们为使模块系统感觉更加直接而采取的第一步。

除了上述针对宏的更改外,use 还有两项更改。第一个是外部 crate 现在位于 prelude 中,即:

// old
let json = ::serde_json::from_str("...");

// new
let json = serde_json::from_str("...");

这里的诀窍是,由于 Rust 模块系统的工作方式,“旧”样式并非总是需要:

extern crate serde_json;

fn main() {
    // this works just fine; we're in the crate root, so `serde_json` is in
    // scope here
    let json = serde_json::from_str("...");
}

mod foo {
    fn bar() {
        // this doesn't work; we're inside the `foo` namespace, and `serde_json`
        // isn't declared there
        let json = serde_json::from_str("...");

    }

    // one option is to `use` it inside the module
    use serde_json;

    fn baz() {
        // the other option is to use `::serde_json`, so we're using an absolute path
        // rather than a relative one
        let json = ::serde_json::from_str("...");
    }
}

将函数移动到子模块并导致您的某些代码中断并不是一个很好的体验。现在,它将检查路径的第一部分,查看它是否是 extern crate,如果是,则无论您在模块层次结构中的哪个位置都将使用它。

最后,use 还支持使用以 crate 开头的路径将项引入作用域

mod foo {
    pub fn bar() {
        // ...
    }
}

// old
use ::foo::bar;
// or
use foo::bar;

// new
use crate::foo::bar;

路径开头的 crate 关键字表示您希望路径从您的 crate 根开始。以前,在 use 之后指定的路径始终从 crate 根开始,但直接引用项的路径将从本地路径开始,这意味着路径的行为不一致。

mod foo {
    pub fn bar() {
        // ...
    }
}

mod baz {
    pub fn qux() {
        // old
        ::foo::bar();
        // does not work, which is different than with `use`:
        // foo::bar();

        // new
        crate::foo::bar();
    }
}

一旦这种风格被广泛使用,这有望使绝对路径更加清晰,并消除一些前导 :: 的丑陋之处。

所有这些更改加在一起,可以更直接地理解路径的解析方式。无论您在 use 语句之外的任何地方看到像 a::b::c 这样的路径,您都可以问:

  • a 是 crate 的名称吗?那么我们正在寻找其中的 b::c
  • a 是关键字 crate 吗?那么我们正在从 crate 的根查找 b::c
  • 否则,我们正在从模块层次结构中的当前位置查找 a::b::c

use 路径始终从 crate 根开始的旧行为仍然适用。但是,在一次性切换到新样式后,这些规则将统一应用于所有位置的路径,并且您在移动代码时需要调整导入的次数会少得多。

原始标识符

您现在可以使用一些新的语法将关键字用作标识符

// define a local variable named `for`
let r#for = true;

// define a function named `for`
fn r#for() {
    // ...
}

// call that function
r#for();

今天这种情况没有太多用例,但当您尝试将 Rust 2015 crate 与 Rust 2018 项目一起使用时,反之亦然,因为两个版本中的关键字集会有所不同;我们将在即将发布的有关 Rust 2018 的博客文章中进行更多解释。

no_std 应用

早在 Rust 1.6 中,我们就宣布了no_stdlibcore 的稳定,用于构建没有标准库的项目。但是,有一个转折:您只能构建库,而不能构建应用程序。

使用 Rust 1.30,您可以使用#[panic_handler] 属性来实现您自己的 panic 处理。现在这意味着您可以构建不使用标准库的应用程序,而不仅仅是库。

其他

最后,您现在可以使用 vis 说明符在宏中匹配可见性关键字(如 pub。此外,像 #[rustfmt::skip] 这样的“工具属性”现在是稳定的。但是,像 #[allow(clippy::something)] 这样的工具 *lint* 尚未稳定。

有关更多信息,请参阅详细发行说明

库的稳定

在此版本中,一些新的 API 已稳定

  • Ipv4Addr::{BROADCAST, LOCALHOST, UNSPECIFIED}
  • Ipv6Addr::{LOCALHOST, UNSPECIFIED}
  • Iterator::find_map

此外,标准库长期以来一直有像 trim_left 这样的函数来消除某些文本一侧的空白。但是,在考虑从右到左 (RTL) 的语言时,“右”和“左”的含义变得令人困惑。因此,我们正在为这些 API 引入新名称:

  • trim_left -> trim_start
  • trim_right -> trim_end
  • trim_left_matches -> trim_start_matches
  • trim_right_matches -> trim_end_matches

我们计划在 Rust 1.33 中弃用(但当然不会删除)旧名称。

有关更多信息,请参阅详细发行说明

Cargo 功能

此版本中 Cargo 的最大特性是,我们现在有了进度条!

demo gif

有关更多信息,请参阅详细发行说明

1.30.0 的贡献者

许多人齐心协力创建了 Rust 1.30。没有大家,我们不可能做到这一点。谢谢!