diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 37f4e711..eb85f178 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -384,3 +384,4 @@ - [1.75](appendix/rust-versions/1.75.md) - [1.76](appendix/rust-versions/1.76.md) - [1.77](appendix/rust-versions/1.77.md) + - [1.78](appendix/rust-versions/1.78.md) diff --git a/src/appendix/rust-versions/1.78.md b/src/appendix/rust-versions/1.78.md new file mode 100644 index 00000000..fa263990 --- /dev/null +++ b/src/appendix/rust-versions/1.78.md @@ -0,0 +1,114 @@ +# Rust 新版解读 | 1.78 | 诊断属性宏 + +> Rust 1.78 官方 release doc: [Announcing Rust 1.78.0 | Rust Blog](https://blog.rust-lang.org/2024/05/02/Rust-1.78.0.html) + +通过 [rustup](https://www.rust-lang.org/tools/install) 安装的同学可以使用以下命令升级到 1.78 版本: + +```shell +$ rustup update stable +``` + +## 诊断属性宏 + +Rust 现在支持 `#[diagnostic]` 属性命名空间,用于影响编译器错误消息。这些被视为提示,编译器不需要使用它们,也不会因为提供了编译器不认识的诊断而报错。这种灵活性允许源代码提供诊断,即使不是所有编译器都支持,无论是不同版本还是完全不同的实现。 + +随着这个命名空间的出现,第一个支持的属性 `#[diagnostic::on_unimplemented]` 也随之而来,可以放在一个 trait 上,用于自定义当需要但未在类型上实现该 trait 时的消息。考虑下面来自[稳定 PR](https://github.com/rust-lang/rust/pull/119888/) 里的示例: + +```rust +#[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 {} + +fn use_my_trait(_: impl ImportantTrait) {} + +fn main() { + use_my_trait(String::new()); +} +``` + +此前,编译器会给出一个内置错误,如下: + +```shell +error[E0277]: the trait bound `String: ImportantTrait` is not satisfied + --> src/main.rs:12:18 + | +12 | use_my_trait(String::new()); + | ------------ ^^^^^^^^^^^^^ the trait `ImportantTrait` is not implemented for `String` + | | + | required by a bound introduced by this call + | +``` + +现在,使用 `#[diagnostic::on_unimplemented]`,自定义消息填充主要错误行,自定义标签放在源输出上。原始标签仍然写在帮助输出中,任何自定义注释也会被写入。 (这些细节未来可能会发生变化。) + +```shell +error[E0277]: My Message for `ImportantTrait` 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` is not implemented for `String` + = note: Note 1 + = note: Note 2 +``` + +对于 trait 作者来说,如果你能提供更好的提示,而不是仅仅给出缺失部分,这种诊断就更有用。例如,这是标准库中的一个示例: + +```rust +#[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 {} +``` + +更多信息,请参考[诊断工具属性命名空间](https://doc.rust-lang.org/stable/reference/attributes/diagnostics.html#the-diagnostic-tool-attribute-namespace)的参考部分。 + +## 不安全前提断言 + +Rust 标准库有许多用于不安全函数前提的断言,但历史上它们只在标准库的 `#[cfg(debug_assertions)]` 构建中启用,以避免影响发布性能。然而,由于标准库通常以发布模式编译和分发,大多数 Rust 开发者根本没有执行这些检查。 + +现在,这些断言的条件被延迟到代码生成,因此它们将根据用户自己对调试断言的设置进行检查(在调试和测试构建中默认启用)。这个变化有助于用户捕获他们代码中的未定义行为。 + +例如,`slice::from_raw_parts` 需要一个对齐的非空指针。下面故意错位指针的使用有未定义行为,虽然如果你运气不好,它可能在过去看起来“工作”,但调试断言现在可以捕获它: + +```rust +fn main() { + let slice: &[u8] = &[1, 2, 3, 4, 5]; + let ptr = slice.as_ptr(); + + // 创建一个 `ptr` 的偏移量,它总是比 `u16` 的正确对齐少一个 + let i = usize::from(ptr as usize & 1 == 0); + + let slice16: &[u16] = unsafe { std::slice::from_raw_parts(ptr.add(i).cast::(), 2) }; + dbg!(slice16); +} +``` + +在调试构建中,这将导致一个 panic: + +```shell +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`](https://doc.rust-lang.org/std/primitive.pointer.html#method.align_offset) 计算改变指针到给定对齐方式所需的偏移量。如果不可能,它将返回 `usize::MAX`,但以前允许它始终返回 `usize::MAX`,现在这种行为被移除。 + +* [`slice::align_to`](https://doc.rust-lang.org/std/primitive.slice.html#method.align_to) 和 [`slice::align_to_mut`](https://doc.rust-lang.org/std/primitive.slice.html#method.align_to_mut) 都将切片转换为对齐的中间切片和剩余的不对齐头和尾切片。这些方法现在承诺返回最大可能的中间部分,而不允许实现返回不那么优化的东西,比如将所有东西作为头切片返回。 + +## Others + +其它更新细节,和稳定的API列表,参考[原Blog](https://blog.rust-lang.org/2024/05/02/Rust-1.78.0.html#stabilized-apis)