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。没有你们,我们不可能做到这一点。谢谢!