宣布 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 被要求但还没有在类型上实现时的消息。考虑 稳定化拉取请求 中给出的示例

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