诊断属性

以下 属性 用于在编译期间控制或生成诊断消息。

Lint 检查属性

Lint 检查命名了一种潜在的不良编码模式,例如无法访问的代码或遗漏的文档。

lint 属性 allowexpectwarndenyforbid 使用 MetaListPaths 语法来指定要更改属性应用实体的 lint 级别的 lint 名称列表。

对于任何 lint 检查 C

  • #[allow(C)] 覆盖对 C 的检查,因此违规行为将不会报告。
  • #[expect(C)] 表示预期会发出 lint C。如果期望未实现,则该属性将抑制 C 的发出或发出警告。
  • #[warn(C)] 警告关于 C 的违规行为,但继续编译。
  • #[deny(C)] 在遇到 C 的违规行为后发出错误信号。
  • #[forbid(C)]deny(C) 相同,但也禁止之后更改 lint 级别。

注意:rustc 支持的 lint 检查可以通过 rustc -W help 找到,以及它们的默认设置,并在 rustc book 中记录。

#![allow(unused)] fn main() { pub mod m1 { // Missing documentation is ignored here #[allow(missing_docs)] pub fn undocumented_one() -> i32 { 1 } // Missing documentation signals a warning here #[warn(missing_docs)] pub fn undocumented_too() -> i32 { 2 } // Missing documentation signals an error here #[deny(missing_docs)] pub fn undocumented_end() -> i32 { 3 } } }

Lint 属性可以覆盖先前属性指定的级别,只要该级别不尝试更改禁止的 lint(deny 除外,它允许在 forbid 上下文中使用,但会被忽略)。先前的属性是指语法树中更高级别的属性,或源文件中从左到右列出的同一实体上的先前属性。

此示例展示了如何使用 allowwarn 来切换特定检查的开启和关闭

#![allow(unused)] fn main() { #[warn(missing_docs)] pub mod m2 { #[allow(missing_docs)] pub mod nested { // Missing documentation is ignored here pub fn undocumented_one() -> i32 { 1 } // Missing documentation signals a warning here, // despite the allow above. #[warn(missing_docs)] pub fn undocumented_two() -> i32 { 2 } } // Missing documentation signals a warning here pub fn undocumented_too() -> i32 { 3 } } }

此示例展示了如何使用 forbid 来禁止对该 lint 检查使用 allowexpect

#![allow(unused)] fn main() { #[forbid(missing_docs)] pub mod m3 { // Attempting to toggle warning signals an error here #[allow(missing_docs)] /// Returns 2. pub fn undocumented_too() -> i32 { 2 } } }

注意:rustc 允许在 命令行 上设置 lint 级别,并且还支持对报告的 lint 设置上限

Lint 原因

所有 lint 属性都支持额外的 reason 参数,以提供添加特定属性的上下文原因。如果 lint 在定义的级别发出,则此原因将显示为 lint 消息的一部分。

#![allow(unused)] fn main() { // `keyword_idents` is allowed by default. Here we deny it to // avoid migration of identifiers when we update the edition. #![deny( keyword_idents, reason = "we want to avoid these idents to be future compatible" )] // This name was allowed in Rust's 2015 edition. We still aim to avoid // this to be future compatible and not confuse end users. fn dyn() {} }

这是另一个示例,其中 lint 被允许并带有一个原因

#![allow(unused)] fn main() { use std::path::PathBuf; pub fn get_path() -> PathBuf { // The `reason` parameter on `allow` attributes acts as documentation for the reader. #[allow(unused_mut, reason = "this is only modified on some platforms")] let mut file_name = PathBuf::from("git"); #[cfg(target_os = "windows")] file_name.set_extension("exe"); file_name } }

#[expect] 属性

#[expect(C)] 属性为 lint C 创建一个 lint 期望。如果同一位置的 #[warn(C)] 属性将导致 lint 发出,则该期望将被实现。如果期望未实现,因为 lint C 不会被发出,则将在该属性处发出 unfulfilled_lint_expectations lint。

fn main() { // This `#[expect]` attribute creates a lint expectation, that the `unused_variables` // lint would be emitted by the following statement. This expectation is // unfulfilled, since the `question` variable is used by the `println!` macro. // Therefore, the `unfulfilled_lint_expectations` lint will be emitted at the // attribute. #[expect(unused_variables)] let question = "who lives in a pineapple under the sea?"; println!("{question}"); // This `#[expect]` attribute creates a lint expectation that will be fulfilled, since // the `answer` variable is never used. The `unused_variables` lint, that would usually // be emitted, is suppressed. No warning will be issued for the statement or attribute. #[expect(unused_variables)] let answer = "SpongeBob SquarePants!"; }

lint 期望仅由被 expect 属性抑制的 lint 发出实现。如果在作用域中使用其他级别属性(如 allowwarn)修改了 lint 级别,则 lint 发出将相应地处理,并且期望将保持未实现状态。

#![allow(unused)] fn main() { #[expect(unused_variables)] fn select_song() { // This will emit the `unused_variables` lint at the warn level // as defined by the `warn` attribute. This will not fulfill the // expectation above the function. #[warn(unused_variables)] let song_name = "Crab Rave"; // The `allow` attribute suppresses the lint emission. This will not // fulfill the expectation as it has been suppressed by the `allow` // attribute and not the `expect` attribute above the function. #[allow(unused_variables)] let song_creator = "Noisestorm"; // This `expect` attribute will suppress the `unused_variables` lint emission // at the variable. The `expect` attribute above the function will still not // be fulfilled, since this lint emission has been suppressed by the local // expect attribute. #[expect(unused_variables)] let song_version = "Monstercat Release"; } }

如果 expect 属性包含多个 lint,则每个 lint 都是单独期望的。对于一个 lint 组,如果组内的一个 lint 已发出就足够了。

#![allow(unused)] fn main() { // This expectation will be fulfilled by the unused value inside the function // since the emitted `unused_variables` lint is inside the `unused` lint group. #[expect(unused)] pub fn thoughts() { let unused = "I'm running out of examples"; } pub fn another_example() { // This attribute creates two lint expectations. The `unused_mut` lint will be // suppressed and with that fulfill the first expectation. The `unused_variables` // wouldn't be emitted, since the variable is used. That expectation will therefore // be unsatisfied, and a warning will be emitted. #[expect(unused_mut, unused_variables)] let mut link = "https://rust-lang.net.cn/"; println!("Welcome to our community: {link}"); } }

注意:#[expect(unfulfilled_lint_expectations)] 的行为当前被定义为始终生成 unfulfilled_lint_expectations lint。

Lint 组

Lint 可以组织成命名组,以便可以一起调整相关 lint 的级别。使用命名组等效于列出该组内的 lint。

#![allow(unused)] fn main() { // This allows all lints in the "unused" group. #[allow(unused)] // This overrides the "unused_must_use" lint from the "unused" // group to deny. #[deny(unused_must_use)] fn example() { // This does not generate a warning because the "unused_variables" // lint is in the "unused" group. let x = 1; // This generates an error because the result is unused and // "unused_must_use" is marked as "deny". std::fs::remove_file("some_file"); // ERROR: unused `Result` that must be used } }

有一个名为 “warnings” 的特殊组,其中包括所有 “warn” 级别的 lint。“warnings” 组忽略属性顺序,并应用于实体内所有原本会发出警告的 lint。

#![allow(unused)] fn main() { unsafe fn an_unsafe_fn() {} // The order of these two attributes does not matter. #[deny(warnings)] // The unsafe_code lint is normally "allow" by default. #[warn(unsafe_code)] fn example_err() { // This is an error because the `unsafe_code` warning has // been lifted to "deny". unsafe { an_unsafe_fn() } // ERROR: usage of `unsafe` block } }

工具 lint 属性

工具 lint 允许使用作用域 lint,以 allowwarndenyforbid 特定工具的 lint。

工具 lint 仅在关联的工具处于活动状态时才会被检查。如果 lint 属性(例如 allow)引用了不存在的工具 lint,则编译器在您使用该工具之前不会警告不存在的 lint。

否则,它们的工作方式与常规 lint 属性完全相同。

// set the entire `pedantic` clippy lint group to warn #![warn(clippy::pedantic)] // silence warnings from the `filter_map` clippy lint #![allow(clippy::filter_map)] fn main() { // ... } // silence the `cmp_nan` clippy lint just for this function #[allow(clippy::cmp_nan)] fn foo() { // ... }

注意:rustc 当前识别 “clippy” 和 “rustdoc” 的工具 lint。

deprecated 属性

deprecated 属性 将项目标记为已弃用。rustc 将在使用 #[deprecated] 项目时发出警告。rustdoc 将显示项目弃用信息,包括 since 版本和 note(如果可用)。

deprecated 属性有几种形式

  • deprecated — 发出通用消息。
  • deprecated = "message" — 在弃用消息中包含给定的字符串。
  • MetaListNameValueStr 语法,带有两个可选字段
    • since — 指定项目被弃用的版本号。rustc 当前不解释该字符串,但像 Clippy 这样的外部工具可能会检查该值的有效性。
    • note — 指定应包含在弃用消息中的字符串。这通常用于提供关于弃用和首选替代方案的解释。

deprecated 属性可以应用于任何 项目trait 项目枚举变体结构体字段外部块项目宏定义。它不能应用于 trait 实现项目。当应用于包含其他项目的项目(例如 模块实现)时,所有子项目都继承弃用属性。

这是一个例子

#![allow(unused)] fn main() { #[deprecated(since = "5.2.0", note = "foo was rarely used. Users should instead use bar")] pub fn foo() {} pub fn bar() {} }

RFC 包含动机和更多详细信息。

must_use 属性

must_use 属性 用于在值未“使用”时发出诊断警告。

must_use 属性可以应用于用户定义的复合类型(structenumunion)、函数trait

must_use 属性可以包含消息,方法是使用 MetaNameValueStr 语法,例如 #[must_use = "example message"]。该消息将与警告一起给出。

当用于用户定义的复合类型时,如果 表达式表达式语句 具有该类型,则会违反 unused_must_use lint。

#![allow(unused)] fn main() { #[must_use] struct MustUse { // some fields } impl MustUse { fn new() -> MustUse { MustUse {} } } // Violates the `unused_must_use` lint. MustUse::new(); }

当用于函数时,如果 表达式表达式语句 是对该函数的 调用表达式,则会违反 unused_must_use lint。

#![allow(unused)] fn main() { #[must_use] fn five() -> i32 { 5i32 } // Violates the unused_must_use lint. five(); }

当用于 trait 声明 时,对返回该 trait 的 impl traitdyn trait 的函数的 调用表达式表达式语句 会违反 unused_must_use lint。

#![allow(unused)] fn main() { #[must_use] trait Critical {} impl Critical for i32 {} fn get_critical() -> impl Critical { 4i32 } // Violates the `unused_must_use` lint. get_critical(); }

当用于 trait 声明中的函数时,当调用表达式是来自 trait 实现的函数时,该行为也适用。

#![allow(unused)] fn main() { trait Trait { #[must_use] fn use_me(&self) -> i32; } impl Trait for i32 { fn use_me(&self) -> i32 { 0i32 } } // Violates the `unused_must_use` lint. 5i32.use_me(); }

当用于 trait 实现中的函数时,该属性不起作用。

注意:包含该值的琐碎的空操作表达式不会违反 lint。示例包括将该值包装在不实现 Drop 的类型中,然后不使用该类型,并成为未使用的 块表达式 的最终表达式。

#![allow(unused)] fn main() { #[must_use] fn five() -> i32 { 5i32 } // None of these violate the unused_must_use lint. (five(),); Some(five()); { five() }; if true { five() } else { 0i32 }; match true { _ => five() }; }

注意:当有意丢弃 must-use 值时,习惯用法是使用带有 _ 模式的 let 语句

#![allow(unused)] fn main() { #[must_use] fn five() -> i32 { 5i32 } // Does not violate the unused_must_use lint. let _ = five(); }

diagnostic 工具属性命名空间

#[diagnostic] 属性命名空间是用于存放影响编译时错误消息的属性的地方。这些属性提供的提示不保证会被使用。

此命名空间中未知的属性被接受,尽管它们可能会为未使用的属性发出警告。此外,已知属性的无效输入通常会发出警告(有关详细信息,请参见属性定义)。这旨在允许在将来添加或丢弃属性并更改输入,从而允许更改,而无需保持无意义的属性或选项工作。

diagnostic::on_unimplemented 属性

#[diagnostic::on_unimplemented] 属性是编译器的提示,用于补充在 trait 是必需的但在类型上未实现的情况下通常生成的错误消息。

该属性应放置在 trait 声明 上,尽管放置在其他位置不是错误。

该属性使用 MetaListNameValueStr 语法来指定其输入,尽管属性的任何格式错误的输入都不会被视为错误,以提供向前和向后兼容性。

以下键具有给定的含义

  • message — 顶级错误消息的文本。
  • label — 错误消息中内联在错误代码中显示的标签文本。
  • note — 提供额外的注释。

note 选项可以出现多次,这将导致发出多条注释消息。

如果任何其他选项出现多次,则相关选项的第一次出现指定实际使用的值。后续的出现会生成警告。

对于任何未知键都会生成警告。

所有三个选项都接受字符串作为参数,使用与 std::fmt 字符串相同的格式进行解释。

带有给定命名参数的格式参数将被替换为以下文本

  • {Self} — 实现 trait 的类型的名称。
  • { GenericParameterName } — 给定泛型参数的泛型参数类型的名称。

任何其他格式参数都会生成警告,但否则将按原样包含在字符串中。

无效的格式字符串可能会生成警告,但否则是允许的,但可能无法按预期显示。格式说明符可能会生成警告,但否则将被忽略。

在此示例中

#[diagnostic::on_unimplemented( message = "My Message for `ImportantTrait<{A}>` 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]: My Message for `ImportantTrait<i32>` implemented for `String` --> src/main.rs:14:18 | 14 | 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

diagnostic::do_not_recommend 属性

#[diagnostic::do_not_recommend] 属性是编译器的提示,用于不将带注释的 trait 实现显示为诊断消息的一部分。

注意:如果您知道建议通常对程序员无用,则抑制建议可能很有用。这种情况通常发生在广泛的、通用的 impl 中。该建议可能会将程序员引向错误的道路,或者 trait 实现可能是您不想公开的内部细节,或者程序员可能无法满足 bounds。

例如,在关于类型未实现所需 trait 的错误消息中,编译器可能会找到一个 trait 实现,如果不是 trait 实现中的特定 bounds,它将满足要求。编译器可能会告诉用户存在一个 impl,但问题在于 trait 实现中的 bounds。#[diagnostic::do_not_recommend] 属性可用于告诉编译器不要告诉用户 trait 实现,而是简单地告诉用户该类型未实现所需的 trait。

该属性应放置在 trait 实现项目 上,尽管放置在其他位置不是错误。

该属性不接受任何参数,尽管意外的参数不会被视为错误。

在以下示例中,有一个名为 AsExpression 的 trait,用于将任意类型转换为 SQL 库中使用的 Expression 类型。有一个名为 check 的方法,它接受 AsExpression

pub trait Expression { type SqlType; } pub trait AsExpression<ST> { type Expression: Expression<SqlType = ST>; } pub struct Text; pub struct Integer; pub struct Bound<T>(T); pub struct SelectInt; impl Expression for SelectInt { type SqlType = Integer; } impl<T> Expression for Bound<T> { type SqlType = T; } impl AsExpression<Integer> for i32 { type Expression = Bound<Integer>; } impl AsExpression<Text> for &'_ str { type Expression = Bound<Text>; } impl<T> Foo for T where T: Expression {} // Uncomment this line to change the recommendation. // #[diagnostic::do_not_recommend] impl<T, ST> AsExpression<ST> for T where T: Expression<SqlType = ST>, { type Expression = T; } trait Foo: Expression + Sized { fn check<T>(&self, _: T) -> <T as AsExpression<<Self as Expression>::SqlType>>::Expression where T: AsExpression<Self::SqlType>, { todo!() } } fn main() { SelectInt.check("bar"); }

SelectInt 类型的 check 方法期望 Integer 类型。使用 i32 类型调用它可以工作,因为它通过 AsExpression trait 转换为 Integer。但是,使用字符串调用它不起作用,并生成一个可能如下所示的错误

error[E0277]: the trait bound `&str: Expression` is not satisfied --> src/main.rs:53:15 | 53 | SelectInt.check("bar"); | ^^^^^ the trait `Expression` is not implemented for `&str` | = help: the following other types implement trait `Expression`: Bound<T> SelectInt note: required for `&str` to implement `AsExpression<Integer>` --> src/main.rs:45:13 | 45 | impl<T, ST> AsExpression<ST> for T | ^^^^^^^^^^^^^^^^ ^ 46 | where 47 | T: Expression<SqlType = ST>, | ------------------------ unsatisfied trait bound introduced here

通过将 #[diagnostic::do_no_recommend] 属性添加到 AsExpression 的通用 impl,消息更改为

error[E0277]: the trait bound `&str: AsExpression<Integer>` is not satisfied --> src/main.rs:53:15 | 53 | SelectInt.check("bar"); | ^^^^^ the trait `AsExpression<Integer>` is not implemented for `&str` | = help: the trait `AsExpression<Integer>` is not implemented for `&str` but trait `AsExpression<Text>` is implemented for it = help: for that trait implementation, expected `Text`, found `Integer`

第一个错误消息包含关于 &strExpression 关系以及通用 impl 中未满足的 trait bound 的一些令人困惑的错误消息。添加 #[diagnostic::do_no_recommend] 后,它不再考虑通用 impl 作为建议。该消息应该更清晰一些,指示字符串无法转换为 Integer