发布 Rust 1.32.0

2019年1月17日 · Rust 发布团队

Rust 团队很高兴发布 Rust 的新版本 1.32.0。Rust 是一门赋能所有人构建可靠且高效软件的编程语言。

如果你之前通过 rustup 安装了 Rust,获取 Rust 1.32.0 非常简单,只需运行

$ rustup update stable

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

另外补充一点,rustup 最近发布了一些新版本!要更新 rustup 本身,运行 rustup self update

1.32.0 稳定版包含哪些内容

Rust 1.32.0 带来了一些体验上的改进,切换了默认的内存分配器,并使更多函数成为 const 函数。请继续阅读了解一些亮点,或查看详细发布说明获取更多信息。

dbg

首先,一项体验上的改进。你是“打印调试器”吗?如果你是,并且在编写代码时想打印出某个值,你需要这样做:

let x = 5;

println!("{:?}", x);

// or maybe even this
println!("{:#?}", x);

这不是最大的障碍,但只是为了显示 x 的值就得写很多东西。此外,这里没有上下文。如果你有多个 println!s,就很难区分哪个是哪个,除非你为每次调用添加自己的上下文,这就需要更多的工作。

在 Rust 1.32.0 中,我们为此目的添加了一个新宏,dbg!

fn main() {
    let x = 5;

    dbg!(x);
}

如果你运行这个程序,会看到:

[src/main.rs:4] x = 5

你会得到调用它的文件和行号,以及名称和值。此外,println! 打印到标准输出(stdout),所以你其实应该使用 eprintln! 打印到标准错误(stderr)。而 dbg! 做得很好,会输出到 stderr

它甚至在更复杂的情况下也能工作。考虑一下这个阶乘例子:

fn factorial(n: u32) -> u32 {
    if n <= 1 {
        n
    } else {
        n * factorial(n - 1)
    }
}

如果我们想调试它,使用 eprintln! 可能会这样写:

fn factorial(n: u32) -> u32 {
    eprintln!("n: {}", n);

    if n <= 1 {
        eprintln!("n <= 1");

        n
    } else {
        let n = n * factorial(n - 1);

        eprintln!("n: {}", n);

        n
    }
}

我们想在每次迭代时记录 n,并为每个分支提供一些上下文。对于 factorial(4),我们看到这样的输出:

n: 4
n: 3
n: 2
n: 1
n <= 1
n: 2
n: 6
n: 24

这可以使用,但不是特别好。也许我们可以改进输出上下文的方式使其更清晰,但这下我们就不是在调试代码本身了,而是在想办法改进我们的调试代码了。

考虑使用 dbg! 的版本:

fn factorial(n: u32) -> u32 {
    if dbg!(n <= 1) {
        dbg!(1)
    } else {
        dbg!(n * factorial(n - 1))
    }
}

我们只需用这个宏包装我们想打印的各种表达式即可。我们得到这样的输出:

[src/main.rs:3] n <= 1 = false
[src/main.rs:3] n <= 1 = false
[src/main.rs:3] n <= 1 = false
[src/main.rs:3] n <= 1 = true
[src/main.rs:4] 1 = 1
[src/main.rs:5] n * factorial(n - 1) = 2
[src/main.rs:5] n * factorial(n - 1) = 6
[src/main.rs:5] n * factorial(n - 1) = 24
[src/main.rs:11] factorial(4) = 24

因为 dbg! 宏返回它正在调试的值,而不是返回 ()eprintln!,所以我们对代码结构完全不用做任何更改。此外,我们还获得了极其有用的输出。

关于这个小宏说了很多,但我们希望它能改善你的调试体验!当然,我们也在继续努力支持 gdb 等工具。

默认移除 jemalloc

很久很久以前,Rust 有一个大型的、类似 Erlang 的运行时。我们选择使用 jemalloc 而不是系统分配器,因为它通常比默认的系统分配器性能更好。随着时间的推移,我们逐渐去掉了越来越多的运行时部分,最终几乎全部移除,但 jemalloc 却没有。我们没有办法选择自定义分配器,所以无法在不给需要 jemalloc 的用户造成退步的情况下真正移除它。

此外,说 jemalloc 一直是默认选项有些以 UNIX 为中心,因为它只在某些平台上是默认选项。值得注意的是,Windows 上的 MSVC 目标早已使用系统分配器。

最后,虽然 jemalloc 通常性能很好,但并非总是如此。此外,它会使每个 Rust 二进制文件增加约 300KB。过去,我们还遇到了与 jemalloc 相关的各种其他问题。一门系统级语言不默认使用系统分配器也感觉有点奇怪。

基于所有这些原因,当 Rust 1.28 提供了一种选择全局分配器的方式后,我们开始计划将默认分配器切换到系统分配器,并允许你通过一个 crate 使用 jemalloc。在 Rust 1.32 中,我们终于完成了这项工作,默认情况下,你的程序将使用系统分配器。

如果你想继续使用 jemalloc,请使用 jemallocator crate。在你的 Cargo.toml 中:

jemallocator = "0.1.8"

并在你的 crate 根文件中:

#[global_allocator]
static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;

就是这样!如果你不需要 jemalloc,它就不会被强制使用,而如果你需要,只需几行代码即可启用。

模块系统的最终改进

在过去两个版本中,我们宣布了模块系统的几项改进。在 1.32.0 和 2018 edition 中,我们引入了最后一项调整。它被称为 “统一路径”,允许以前无效的导入路径声明以与非导入路径完全相同的方式解析。例如:

enum Color { Red, Green, Blue }

use Color::*;

这段代码之前是无法编译的,因为 use 语句必须以 superselfcrate 开头。现在编译器支持统一路径后,这段代码就能工作了,并且会实现你可能期望的功能:导入在 use 语句上方定义的 Color 枚举的变体。

随着这项改动的到位,我们已经完成了对模块系统的修订工作。希望你目前为止一直喜欢这个简化的系统!

宏改进

Rust 1.32.0 中加入了一些宏的改进。首先,添加了一个新的 literal 匹配器

macro_rules! m {
    ($lt:literal) => {};
}

fn main() {
    m!("some string literal");
}

literal 匹配任何类型的字面量;字符串字面量、数字字面量、char 字面量。

在 2018 edition 中,macro_rules 宏也可以使用 ?,像这样:

macro_rules! bar {
    ($(a)?) => {}
}

? 将匹配模式的零次或一次重复,类似于已有的 * 表示“零次或多次”以及 + 表示“一次或多次”。

标准库稳定化

我们上面谈到了 dbg! 宏,这是一个重要的标准库新增功能。除此之外,有 19 个函数被设置为 const fn,并且所有整数基本类型现在都提供了与指定字节序的字节数组相互转换的函数。这六个函数命名为 to_<endian>_bytesfrom_<endian>_bytes,其中 <endian> 是以下之一:

  • ne - 原生字节序
  • le - 小端字节序
  • be - 大端字节序

查看详细发布说明获取更多详情。

Cargo 特性

Cargo 新增了 cargo c 作为 cargo check 的别名,现在允许在注册表 URL 中包含用户名

查看详细发布说明获取更多信息。

1.32.0 贡献者

许多人齐心协力创建了 Rust 1.32.0。没有你们,我们不可能做到。 感谢!