宣布 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!,很难区分它们,除非您为每个调用添加自己的上下文,这需要更多工作。

在 Rust 1.32.0 中,我们添加了一个新的宏 dbg! 来实现这个目的

fn main() {
    let x = 5;

    dbg!(x);
}

如果您运行此程序,您将看到

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

您将获得调用此宏的文件和行号,以及名称和值。此外,println! 打印到标准输出,因此您实际上应该使用 eprintln! 打印到标准错误。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 版中进行最后一次调整。它被称为“统一路径”,它允许以前无效的导入路径语句以与非导入路径完全相同的方式解析。例如

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 版中,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。没有你们,我们不可能做到。感谢!