Rust 1.78.0 版本发布

2024年5月2日 · Rust 发布团队

Rust 团队很高兴地宣布 Rust 的新版本 1.78.0。Rust 是一种旨在赋能所有人的编程语言,用于构建可靠且高效的软件。

如果您之前通过 rustup 安装了 Rust,您可以使用以下命令获取 1.78.0 版本:

$ rustup update stable

如果您尚未安装,您可以从我们网站上的相应页面获取 rustup,并查看1.78.0 版本的详细发行说明

如果您想通过测试未来的版本来帮助我们,您可以考虑将本地更新为使用 beta 频道 (rustup default beta) 或 nightly 频道 (rustup default nightly)。请报告您可能遇到的任何错误!

1.78.0 稳定版的新特性

诊断属性

Rust 现在支持 #[diagnostic] 属性命名空间,以影响编译器错误消息。这些属性被视为提示,编译器并非必须使用它们,并且提供编译器无法识别的诊断也不是错误。这种灵活性允许源代码提供诊断,即使并非所有编译器都支持这些诊断,无论是不同的版本还是完全不同的实现。

随着这个命名空间的引入,第一个受支持的属性 #[diagnostic::on_unimplemented] 也随之而来,它可以放置在 trait 上,以自定义当需要该 trait 但类型尚未实现时显示的消息。请看稳定化 pull request中给出的示例

#[diagnostic::on_unimplemented(
    message = "My Message for `ImportantTrait<{A}>` is not implemented for `{Self}`",
    label = "My Label",
    note = "Note 1",
    note = "Note 2"
)]
trait ImportantTrait<A> {}

fn use_my_trait(_: impl ImportantTrait<i32>) {}

fn main() {
    use_my_trait(String::new());
}

以前,编译器会给出如下内置错误:

error[E0277]: the trait bound `String: ImportantTrait<i32>` is not satisfied
  --> src/main.rs:12:18
   |
12 |     use_my_trait(String::new());
   |     ------------ ^^^^^^^^^^^^^ the trait `ImportantTrait<i32>` is not implemented for `String`
   |     |
   |     required by a bound introduced by this call
   |

使用 #[diagnostic::on_unimplemented],其自定义消息会填充主要错误行,并且其自定义标签会放置在源代码输出上。原始标签仍然作为帮助输出写入,并且任何自定义注释也会被写入。(这些确切的细节可能会发生变化。)

error[E0277]: My Message for `ImportantTrait<i32>` is not implemented for `String`
  --> src/main.rs:12:18
   |
12 |     use_my_trait(String::new());
   |     ------------ ^^^^^^^^^^^^^ My Label
   |     |
   |     required by a bound introduced by this call
   |
   = help: the trait `ImportantTrait<i32>` is not implemented for `String`
   = note: Note 1
   = note: Note 2

对于 trait 作者来说,如果您能提供比仅仅谈论缺失的实现本身更好的提示,这种诊断会更有用。例如,这是一个来自标准库的节选示例:

#[diagnostic::on_unimplemented(
    message = "the size for values of type `{Self}` cannot be known at compilation time",
    label = "doesn't have a size known at compile-time"
)]
pub trait Sized {}

有关更多信息,请参阅关于diagnostic 工具属性命名空间的参考章节。

断言 unsafe 前置条件

Rust 标准库为 unsafe 函数的前置条件提供了许多断言,但历史上它们仅在标准库的 #[cfg(debug_assertions)] 构建中启用,以避免影响发布性能。然而,由于标准库通常以发布模式编译和分发,因此大多数 Rust 开发者根本没有执行过这些检查。

现在,这些断言的条件被延迟到代码生成阶段,因此将根据用户自己的调试断言设置进行检查——在调试和测试构建中默认启用。此更改有助于用户捕获代码中的未定义行为,尽管检查多少细节通常是不稳定的。

例如,slice::from_raw_parts 需要对齐的非空指针。以下故意使用未对齐指针的行为是未定义的,虽然如果您不走运,它过去可能看起来“有效”,但调试断言现在可以捕获它

fn main() {
    let slice: &[u8] = &[1, 2, 3, 4, 5];
    let ptr = slice.as_ptr();

    // Create an offset from `ptr` that will always be one off from `u16`'s correct alignment
    let i = usize::from(ptr as usize & 1 == 0);
    
    let slice16: &[u16] = unsafe { std::slice::from_raw_parts(ptr.add(i).cast::<u16>(), 2) };
    dbg!(slice16);
}
thread 'main' panicked at library/core/src/panicking.rs:220:5:
unsafe precondition(s) violated: slice::from_raw_parts requires the pointer to be aligned and non-null, and the total size of the slice not to exceed `isize::MAX`
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
thread caused non-unwinding panic. aborting.

确定性重对齐

标准库有一些函数可以更改指针和切片的对齐方式,但如果严格按照它们的文档进行操作,它们之前存在一些使其在实践中难以依赖的注意事项。这些注意事项主要作为对 const 求值的对冲,但无论如何它们仅对非 const 用途稳定。现在承诺根据它们的实际输入具有一致的运行时行为。

  • pointer::align_offset 计算将指针更改为给定对齐方式所需的偏移量。如果不可能,它会返回 usize::MAX,但以前允许始终返回 usize::MAX,现在已删除该行为。

  • slice::align_toslice::align_to_mut 都将切片转换为对齐的中间切片以及剩余的未对齐的头部和尾部切片。这些方法现在承诺返回尽可能大的中间部分,而不是允许实现返回不太理想的结果,例如将所有内容作为头部切片返回。

稳定的 API

这些 API 现在在 const 上下文中是稳定的

兼容性说明

  • 正如先前宣布的,Rust 1.78 增加了以下目标平台的最低 Windows 版本要求至 Windows 10
    • x86_64-pc-windows-msvc
    • i686-pc-windows-msvc
    • x86_64-pc-windows-gnu
    • i686-pc-windows-gnu
    • x86_64-pc-windows-gnullvm
    • i686-pc-windows-gnullvm
  • Rust 1.78 已将其捆绑的 LLVM 升级到版本 18,完成了针对 x86-32 和 x86-64 目标的u128/i128 ABI 更改。使用早于 18 版本的 LLVM 的发行商可能仍然会遇到该帖子中提到的调用约定错误。

其他更改

查看 RustCargoClippy 中更改的所有内容。

1.78.0 版本的贡献者

许多人共同努力创建了 Rust 1.78.0。没有你们所有人,我们不可能做到。谢谢!