Rust 2024 Edition 即将发布!

关注了就能看到更多这么棒的文章哦~

The Rust 2024 Edition takes shape

By Daroc Alden
January 24, 2025
Gemini-1.5-flash translation
https://lwn.net/Articles/1002456/

去年,LWN介绍了Rust 2024 版本(Edition)中计划要做的改动。现在,随着该版本即将在二月份稳定发布,是时候回顾一下版本的迭代过程,看看哪些变更成功落地,又有哪些新的改动被添加进来,以及还有哪些工作有待完成。令人惊讶的是,在过去的一年中,大量的新的工作被提出、实现并稳定化。

版本(Editions)是 Rust 确保稳定性的机制,它能够应对语言频繁的小版本发布以及快速发展的现状。每个版本都代表着一个向后兼容性保证:一旦版本最终确定,那么在这个版本上可以编译的代码将始终在这个版本上可以编译。版本并非完全冻结——只要向后兼容,语言仍然可以添加新特性——但该项目非常重视向后兼容性承诺。编译器的新版本会在大多数发布的 Rust 代码(在crates.io上)和 Rust for Linux 内核代码上进行测试,以确保它们不会破坏为旧版本编写的代码。

因此,新的版本的引入代表着语言引入不兼容变更的唯一机会。在过去的一年里,积累了相当数量的此类变更,从对语法的细微调整,到如何解释类型系统中某些类型的解读方式等重大变更。虽然这些变更中没有一项需要 Rust 项目立即采用,但在编写 Rust 代码时,我们需要注意这些变更。

范围变更

长期以来,Rust 程序员一直被一个问题困扰,那就是该语言如何处理临时值的生命周期。例如,`if let` 结构可以供程序员设置临时值。然而,在当前版本的 Rust 中,它有一个令人讨厌的特性:它会将参与匹配的任何临时值持续保持住,直到相应的`else`块之后。例如,在以下代码中,`std::sync::Mutex`会在`else`分支中保持锁定(locked)状态:

if let Some(x) = mutex.lock().some_method() {    ...} else {    // Mutex remains locked here}

对这一点进行改变,就是一个向后不兼容的变更,因为 Rust 程序可能对其被丢弃的顺序很敏感。具体来说,任何依赖于`mutex`在`else`块中保持 lock 状态的程序,如果 Rust 语言规范更改为更早地丢弃该临时值,则会导致程序逻辑出错。该变更即将在2024版本中启用,同时还将添加一个 lint 规则来警告程序员受到影响的程序。

该项目还修复了块末尾临时表达式的清理顺序。Rust 块具有返回值,这使得`if`语句可以用作表达式,或者用作其他一些情况。但是,当从块返回表达式时,它可能会创建临时值。例如,以下代码为`cell.borrow()`创建了一个临时值:

fn f() -> usize {    let cell = RefCell::new("..");    // The borrow checker throws an error for the below    // line in the 2021 edition, but not the 2024 edition    cell.borrow().len()}

目前,该临时值在块中的局部变量(在本例中仅为`cell`)被丢弃后就马上被丢弃。这有些不方便,因为它意味着上述代码会被生命周期检查器拒绝,因为使用`cell`的地方存活的时间比`cell`本身更长。程序员必须通过显式地将任何临时变量赋值给变量来解决这个问题,这会使用不必要的`let`语句使代码混乱。因此,2024 版本将通过确保块末尾的临时变量在局部变量之前被丢弃来解决这个问题。

不安全代码

将针对不安全函数中的不安全操作发出新的警告。在当前版本的 Rust 中,将函数标记为`unsafe`实际上混淆了`unsafe`关键字的两种含义:它既将函数标记为不安全调用,又允许程序员在函数体中使用不安全操作。虽然这在直觉上是有道理的,但 Rust 程序员普遍发现它会导致混淆,因为它可能使难以发现函数体中哪些操作实际上是不安全的。在新版本中,在不使用额外的`unsafe`块标记用法的`unsafe`函数中使用不安全操作将导致警告。

然而,本版本对`unsafe`规则的最大更改在于该语言如何处理`extern`块(它声明可以使用 C 外部函数接口调用的外部函数)。目前,所有外部函数都被视为不安全的,因为 C 代码可能执行任何操作。对于实际上安全调用的简单函数来说,这有点不符合人体工程学。但是,仅仅让程序员断言某些外部函数是安全的将会破坏 Rust 的承诺:当出现未定义行为时,必须有一个或多个不正确的`unsafe`块负责。

语言开发者采用的解决方案是`unsafe extern`块。现在,整个`extern`声明将被标记为不安全——从而用户会得到应有的警告,即他们必须确保所有外部函数声明(包括每个函数的安全性)都是正确的,否则将面临未定义行为的风险。由于该警告已移动到声明处,因此使用外部函数不再一定需要使用`unsafe`块(尽管程序员仍然可以显式地将外部函数标记为不安全)。实际上,由于许多 Rust 项目使用自动生成的 C 绑定,因此可能没有多少区别。即使该更改还会增加追踪任何有问题的 unsafe 代码的间接步骤,它也会减少用于调用外部函数的不必要的`unsafe`块的数量。

该版本还将使std::env::set_var()和std::env::remove_var()环境变量操作函数不安全,因为它们只应该在单线程环境中使用,但是它们的类型中没有任何内容强制执行这一点。类似地,该版本将要求`no_mangle`属性(它禁用名称修饰)被标记为不安全,因为它如果存在两个同名函数,则可能会导致在某些平台上调用错误的函数实现。它也将不再允许获取对全局可变变量的引用——相反,必须使用不安全块中的原始指针来操作它们。

捕获生命周期信息

LWN 已经报道过改进推断函数返回类型(inferred function return types)可用性的工作,自该文章发表以来,这项工作并没有停止。简要回顾一下:Rust 编译器允许程序员声明函数返回某个 trait 的`impl Trait`,编译器将自动推断函数应该返回哪个具体的类型。这是一种存在类型,对于返回复杂迭代器的函数特别有用,这些迭代器具有复杂的类型,让用户写出来的话就显得很多余。Rust 开发者将此特性称为“return-position impl Trait”(RPIT)类型。

当此特性必须与 Rust 的生命周期系统配合起来时,问题就出现了。如果编译器推断出的类型包含具有给定生命周期的引用,则之前的 Rust 版本无法让程序员“命名”该生命周期,因此程序员也无法向其添加任何边界。这对于经常需要保存具有复杂生命周期的引用的异步函数来说尤其成问题。为了解决这个问题,该语言添加了一个新的语法部分,允许程序员命名在`impl Trait`类型中使用的生命周期。例如,此函数明确指出生命周期'a 被(隐式)返回类型引用,允许程序员根据需要使用名称处理生命周期:

fn example<'a>(one: &'a Foo, two: &Bar) -> impl use<'a> Sized {    ...}

`use`块中未提及的任何生命周期都不会被捕获,因此在上面的示例中,`two`仅在函数内部使用,而不是存储在其返回类型中。这解决了 RPIT 类型的主要问题之一,并且是改进异步函数体验的一大步,这是 Rust 项目 2024 年的目标之一。但`impl Trait`类型的语义目前有点不一致。当存在`use`块时,返回类型捕获的生命周期和泛型类型参数都必须显式列出。当不存在`use`块时,泛型类型参数会被隐式捕获,但生命周期参数不会(因为这可以通过给它们命名来避免整个问题)。

为了使`impl Trait`类型更加一致,该语言的 2024 版本将对生命周期参数和泛型类型参数使用相同的规则:默认情况下,它们将被隐式捕获,如果程序员想要为它们命名(或仅捕获其中一部分),则必须使用`use`块。

小的变更

Cargo(Rust 的包管理器和构建系统)也有一些变化。最大的一项是它将很快支持选择具有兼容最小支持 Rust 版本的依赖项。因此,例如,声明支持 Rust 版本 1.76 的库将不再能够基于仅声明支持 Rust 版本 1.80 的库运行。这将解决维护人员的许多难题。

还有许多对语言的细微更改,这些更改不太可能需要更改现有代码:

  • `Future`和`IntoFuture`特性将无需导入即可使用。

  • 声明宏匹配语法片段的规则正在调整,并变得更加严格。

  • Rustdoc 测试应该运行更快。

  • Rust 的代码格式化程序将以不同的方式对导入进行排序(参见问题124764、123800和123802),并支持多个版本。

  • `gen`关键字(将用于指示生成器)已保留供将来使用,无保护的前缀字符串字面量也是如此。

  • “never”类型(`!=`)将不那么特殊。

  • 程序员将能够迭代装箱切片(iterate through boxed slices)。

尽管有大量的向后不兼容的更改即将在这个版本中发布,但适应这个版本应该不会太难。任何为版本提出的更改的要求之一是,必须有一种方法可以自动迁移现有代码。任何使用 Cargo 的 Rust 项目都应该能够运行`cargo fix –edition`,然后更新其`Cargo.toml`文件中的版本声明以进行升级。一些自动修复有点难看(或引入了新的`unsafe`块),因此该过程并非完全没有痛苦。但是,由于 Rust 会愉快地混合使用不同版本的库,因此许多项目会选择在一段时间内停留在 2021 版。

Rust 项目并没有完全实现它为2024年设定的所有目标(这或许解释了为什么 2024 版本将在 2025 年 2 月发布),但它取得了实质性进展。而且,尽管许多人担心语言复杂性的逐渐累积,但 2024 版本的计划表明,该项目愿意随着时间的推移简化和精简事物——尽管不像有些人希望的那样快。我们将在未来几年看看为 2027 版本提出了什么。

全文完
LWN 文章遵循 CC BY-SA 4.0 许可协议。

欢迎分享、转载及基于现有协议再创作~

长按下面二维码关注,关注 LWN 深度文章以及开源社区的各种新近言论~

9d2788eea8866298a11917fc811ae092.jpeg

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值