宣布 Rust 1.40.0

2019 年 12 月 19 日 · Rust 发布团队

Rust 团队很高兴宣布 Rust 的新版本 1.40.0。Rust 是一种编程语言,它赋予每个人构建可靠高效软件的能力。

如果您之前通过 rustup 安装了 Rust 的旧版本,获取 Rust 1.40.0 非常简单,只需

$ rustup update stable

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

1.40.0 稳定版中的新内容

Rust 1.40.0 的亮点包括 #[non_exhaustive] 以及对 macros!()#[attribute] 的改进。最后,借用检查迁移警告在 Rust 2015 中已成为硬错误。有关更多信息,请查看 详细的发布说明

#[non_exhaustive] 结构体、枚举和变体

假设您是 crate alpha 的库作者,它有一个 pub struct Foo。您希望将 alpha::Foo 的字段也设为 pub,但您不确定是否会在将来的版本中向 Foo 添加更多字段。因此,您现在面临一个困境:要么将字段设为私有,这会带来相应的缺点,要么冒着用户依赖于确切字段的风险,当您添加一个新字段时,他们的代码就会出现问题。Rust 1.40.0 引入了一种方法来打破僵局:#[non_exhaustive]

#[non_exhaustive] 属性附加到 structenum 的变体时,它将阻止定义它的 crate 之外的代码构建该 struct 或变体。为了避免将来出现问题,其他 crate 也被禁止对字段进行穷举匹配。以下示例说明了依赖于 alphabeta 中的错误

// alpha/lib.rs:

#[non_exhaustive]
struct Foo {
    pub a: bool,
}

enum Bar {
    #[non_exhaustive]
    Variant { b: u8 }
}

fn make_foo() -> Foo { ... }
fn make_bar() -> Bar { ... }

// beta/lib.rs:

let x = Foo { a: true }; //~ ERROR
let Foo { a } = make_foo(); //~ ERROR

// `beta` will still compile when more fields are added.
let Foo { a, .. } = make_foo(); //~ OK


let x = Bar::Variant { b: 42 }; //~ ERROR
let Bar::Variant { b } = make_bar(); //~ ERROR
let Bar::Variant { b, .. } = make_bar(); //~ OK
                   // -- `beta` will still compile...

幕后发生的事情是,#[non_exhaustive] structenum 变体的构造函数的可见性降低到 pub(crate),从而阻止了定义它的 crate 之外的访问。

#[non_exhaustive] 可能更重要的一个方面是,它也可以附加到 enum 本身。一个例子,来自标准库,是 Ordering

#[non_exhaustive]
pub enum Ordering { Relaxed, Release, Acquire, AcqRel, SeqCst }

在这种情况下,#[non_exhaustive] 的目的是确保随着时间的推移可以添加更多变体。这是通过阻止其他 crate 对 Ordering 进行穷举模式匹配来实现的。也就是说,编译器会拒绝

match ordering {
    // This is an error, since if a new variant is added,
    // this would suddenly break on an upgrade of the compiler.
    Relaxed | Release | Acquire | AcqRel | SeqCst => {
        /* logic */
    }
}

相反,其他 crate 需要通过添加使用例如 _ 的通配符分支来考虑更多变体的可能性

match ordering {
    Relaxed | Release | Acquire | AcqRel | SeqCst => { /* ... */ }
    // OK; if more variants are added, nothing will break.
    _ => { /* logic */ }
}

有关 #[non_exhaustive] 属性的更多详细信息,请查看 稳定性报告

宏和属性改进

在 1.40.0 中,我们对宏和属性进行了一些改进,包括

  • 在类型上下文中调用过程宏 mac!()

    例如,您可以编写 type Foo = expand_to_type!(bar);,其中 expand_to_type 将是一个过程宏。

  • extern { ... } 块中的宏。

    这包括 bang!() 宏,例如

    macro_rules! make_item { ($name:ident) => { fn $name(); } }
    
    extern {
        make_item!(alpha);
        make_item!(beta);
    }
    

    现在还支持 extern { ... } 中项目上的过程宏属性

    extern "C" {
        // Let's assume that this expands to `fn foo();`.
        #[my_identity_macro]
        fn foo();
    }
    
  • 在过程宏中生成 macro_rules! 项目。

    函数式 (mac!()) 和属性 (#[mac]) 宏现在都可以生成 macro_rules! 项目。有关卫生方面的详细信息,请参阅附带的稳定性报告。

  • $m:meta 匹配器 支持 任意令牌流值

    也就是说,以下现在有效

    macro_rules! accept_meta { ($m:meta) => {} }
    accept_meta!( my::path );
    accept_meta!( my::path = "lit" );
    accept_meta!( my::path ( a b c ) );
    accept_meta!( my::path [ a b c ] );
    accept_meta!( my::path { a b c } );
    

借用检查迁移警告在 Rust 2015 中已成为硬错误

在 1.35.0 版本中,我们宣布,NLL 在 Rust 2015 中推出,此前它是在 Rust 1.31 中为 2018 版发布的。

正如我们当时指出的,旧的借用检查器存在一些错误,这些错误会导致内存不安全,而 NLL 借用检查器修复了这些错误。由于这些修复会破坏一些稳定代码,我们决定逐步引入这些错误,方法是检查旧的借用检查器是否接受程序,而 NLL 检查器是否拒绝程序。在这些情况下,错误将降级为警告。

之前的版本 Rust 1.39.0 将这些警告更改为使用 2018 版 的代码的错误。Rust 1.40.0 对 2015 版 的用户应用了相同的更改,永久地堵住了这些健全性漏洞。这也使我们能够 清理编译器中的旧代码

如果您的构建由于此更改而中断,或者您想了解更多信息,请查看 Niko Matsakis 的博客文章

标准库中更多 const fn

在 Rust 1.40.0 中,以下函数已成为 const fn

标准库的添加

在 Rust 1.40.0 中,以下函数和宏已稳定

其他更改

Rust 1.40.0 版本中还有其他更改:查看 RustCargoClippy 中的更改。

另请参阅 兼容性说明,以查看您是否受到这些更改的影响。

1.40.0 的贡献者

许多人共同创建了 Rust 1.40.0。没有你们,我们无法做到。 感谢!