第29章 Cargo Lint 详解
注意:Cargo 的 Lint 系统尚不稳定,仅能在 nightly 工具链上使用
| 分组 | 说明 | 默认级别 |
|---|---|---|
cargo::default | 所有默认开启的 Lint(correctness、suspicious、style、complexity、perf) | warn/deny |
cargo::correctness | 代码存在明显错误或无用 | deny |
cargo::complexity | warn | |
cargo::perf | warn | |
cargo::style | warn | |
cargo::suspicious | warn | |
cargo::nursery | allow | |
cargo::pedantic | allow | |
cargo::restriction | allow |
默认允许
以下 Lint 默认设置为“允许”级别。
implicit_minimum_version_reqnon_kebab_case_featuresnon_kebab_case_packagesnon_snake_case_featuresnon_snake_case_packages
默认警告
以下 Lint 默认设置为“警告”级别。
blanket_hint_mostly_unusedmissing_lints_inheritancenon_kebab_case_binsredundant_homepageredundant_readmeunknown_lintsunused_dependenciesunused_workspace_dependenciesunused_workspace_package_fields
默认禁止(deny-by-default)
这些 lint 默认都设为 'deny' 级别。
blanket_hint_mostly_unused
- 分组:
suspicious - 级别:
warn - MSRV:
1.79.0
作用
检查 hint-mostly-unused 是否被应用到了所有依赖上。
为什么这是坏味道
hint-mostly-unused 表示一个 crate 的大部分 API 不会被下游使用者用到;这个提示可以尽量减少完全未被使用的条目的编译时间,从而加快构建。但如果把这个提示误用到不符合该条件的 crate 上,反而会拖慢构建。应该只把它选择性地应用在符合条件的依赖上,全局启用必然是误用,很可能导致构建变慢。
示例
[profile.dev.package."*"]
hint-mostly-unused = true
应改为:
[profile.dev.package.huge-mostly-unused-dependency]
hint-mostly-unused = true
implicit_minimum_version_req
- 分组:
pedantic - 级别:
allow
作用
检查没有显式写出完整 major.minor.patch 版本要求的依赖声明,比如 serde = "1" 或 serde = "1.0"。
这个 lint 目前只针对插入符(caret)要求(即默认版本要求)。
为什么这是坏味道
版本要求如果不指明完整的语义化版本号,可能会误导读者对实际最低支持版本的认知。例如,serde = "1" 隐含的最低版本限制是 1.0.0。如果你的代码实际上依赖 1.0.219 引入的功能,那么 1.0.0 这个隐含的最低版本就会给出错误的兼容性印象。
明确指定完整版本号有助于:
- 准确记录最低版本要求
- 更好地兼容
-Z minimal-versions选项 - 为依赖方提供更清晰的版本约束
缺点
即便指定了完整版本号,如果缺乏测试,最低版本限制仍可能是错误的。该 lint 能帮助你显式声明最低版本要求,但无法保证其准确性。
示例
[dependencies]
serde = "1"
应改写为完整的特定版本:
[dependencies]
serde = "1.0.219"
missing_lints_inheritance
- 分组:
suspicious - 级别:
warn - MSRV:
1.79.0
功能说明
检查在存在 workspace.lints 配置的情况下,哪些包缺少 lints 表格。
为什么值得关注
许多人误以为 workspace.lints 会被自动隐式继承,但实际上并非如此。
缺点
示例
[workspace.lints.cargo]
应改写为:
[workspace.lints.cargo]
[lints]
workspace = true
或者,如果你想明确表示不打算继承工作区的 lint 规则,可以添加一个空的 [lints] 表格:
[workspace.lints.cargo]
[lints]
non_kebab_case_bins
- 分组:
style - 级别:
warn - MSRV:
1.79.0
功能说明
检测不符合 kebab-case 命名规范的二进制可执行文件名称(无论是显式还是隐式定义的)。
为什么值得关注
命令行工具通常遵循 kebab-case 命名约定。
缺点
更改二进制文件名称会给现有用户带来不便。
二进制文件可能需要遵循外部控制的规范,其中可能包含不同的命名约定。
GUI 应用程序可能希望选择更注重用户体验的命名规范,如“标题大写”或“句子首字母大写”。
示例
[[bin]]
name = "foo_bar"
应写作:
[[bin]]
name = "foo-bar"
non_kebab_case_features
- 分组:
restriction - 级别:
allow
功能说明
检测非 kebab-case 风格的 feature 名称。
为何不妥
同一工作区内存在多种命名风格容易造成混淆。
缺点
用户通常期望与某个依赖紧密耦合的 feature 名称与该依赖的名称保持一致。
示例
[features]
foo_bar = []
应写作:
[features]
foo-bar = []
non_kebab_case_packages
- 分组:
restriction - 级别:
allow
功能说明
检测非 kebab-case 风格的包名称。
为何不妥
同一工作区内存在多种命名风格容易造成混淆。
缺点
用户需要在脑海中将包名称转换为 Rust 中的命名空间。
示例
[package]
name = "foo_bar"
应写作:
[package]
name = "foo-bar"
non_snake_case_features
- 分组:
restriction - 级别:
allow
功能说明
检测非 snake-case 风格的 feature 名称。
为何不妥
同一工作区内存在多种命名风格容易造成混淆。
缺点
用户通常期望与某个依赖紧密耦合的 feature 名称与该依赖的名称保持一致。
示例
[features]
foo-bar = []
应写成:
[features]
foo_bar = []
non_snake_case_packages
- 分组:
restriction - 级别:
allow
作用
检测不是 snake_case 的包名。
为什么不好
在同一个 workspace 中混用多种命名风格会让人困惑。
缺点
使用时需要在脑中把包名转换为 Rust 的命名空间名。
示例
[package]
name = "foo_bar"
应写成:
[package]
name = "foo-bar"
redundant_homepage
- 分组:
style - 级别:
warn - MSRV:
1.79.0
作用
检查 package.homepage 的值是否已可由其他字段推导得出。
为什么不好
包页面在渲染每个链接时,多余的链接只会带来视觉噪音。
缺点
示例
[package]
name = "foo"
homepage = "https://github.com/rust-lang/cargo/"
repository = "https://github.com/rust-lang/cargo/"
应写成:
[package]
name = "foo"
repository = "https://github.com/rust-lang/cargo/"
redundant_readme
- 分组:
style - 级别:
warn - MSRV:
1.79.0
作用
检查那些可以自动推断出来的 package.readme 字段。
为什么不好
徒增样板代码。
缺点
用户可能不清楚自己的文件命名是否符合自动推断的规则。
示例
[package]
name = "foo"
readme = "README.md"
应写为:
[package]
name = "foo"
text_direction_codepoint_in_comment
- 分组:
correctness - 级别:
deny - MSRV:
1.79.0
功能说明
检测清单注释中那些在视觉上改变屏幕文本显示效果、但与内存中实际表示不一致的 Unicode 码点。
潜在风险
Unicode 支持调整屏幕上的文本视觉流向,以适配从右向左书写的脚本。但经过特制的注释可能会让本应编译的代码看起来像是注释的一部分,具体取决于读取代码的软件环境。为避免此类潜在问题或混淆(例如 CVE-2021-42574 所述的漏洞),默认策略禁止使用这类码点。
text_direction_codepoint_in_literal
- 分组:
correctness - 级别:
deny - MSRV:
1.79.0
功能说明
检测清单字面量中那些在视觉上改变屏幕文本显示效果、但与内存中实际表示不一致的 Unicode 码点。
潜在风险
Unicode 支持调整屏幕上的文本视觉流向,以适配从右向左书写的脚本。但经过特制的字面量可能会让本应编译的代码看起来像是字面量的一部分,具体取决于读取代码的软件环境。为避免此类潜在问题或混淆(例如 CVE-2021-42574 所述的漏洞),默认策略禁止使用这类码点。
unknown_lints
- 分组:
suspicious - 级别:
warn - MSRV:
1.79.0
功能说明
检查 [lints.cargo] 表中是否存在未知的 lints。
潜在风险
- Lint 名称可能存在拼写错误,导致无法按预期工作且难以排查原因。
- 如果
cargo未来引入了同名 lint,当前未知的 lint 可能会引发错误。
示例
[lints.cargo]
this-lint-does-not-exist = "warn"
unused_dependencies
- 分组:
style - 级别:
warn - MSRV:
1.79.0
作用
检查没有任何 cargo target 使用到的依赖。
为什么不好
会拖慢编译速度。
局限性
这个 lint 只在特定情况下触发:不同的依赖表对应不同的 cargo target,必须全部构建之后才能判断某个依赖是否未被使用。目前它只检查被选中的包,不像大多数 lint 那样检查所有 path 依赖。cargo target 的选择参数(与选择了哪些包无关)决定了检查哪些依赖表。由于没有办法选中所有使用 [dev-dependencies] 的 cargo target,这类依赖不会被检查。
示例如下:
cargo check会检查[build-dependencies]和[dependencies]cargo check --all-targets仍然只检查[build-dependencies]和[dependencies],不会检查[dev-dependencoes]cargo check --bin foo不会检查[dependencies],即使foo是唯一的 bin(但[build-dependencies]会被检查)cargo check -p foo不会检查path依赖bar的任何依赖表,即使bar只有一个[lib]
如果是为了启用某个 feature 而依赖一个传递依赖,可能会产生误报。
如果因为需要在 Cargo.toml 中固定某个传递依赖的版本而出现误报,可以把该依赖移到 target."cfg(false)".dependencies 表中。
示例
[package]
name = "foo"
[dependencies]
unused = "1"
应改为:
[package]
name = "foo"
unused_workspace_dependencies
- 分组:
suspicious - 级别:
warn - MSRV:
1.79.0
作用
检查 [workspace.dependencies] 中未被继承的条目
为什么有害
它们可能误导使用者,让人误以为这些依赖已被使用
示例
[workspace.dependencies]
regex = "1"
[dependencies]
unused_workspace_package_fields
- 分组:
suspicious - 级别:
warn - MSRV:
1.79.0
功能说明
检查 [workspace.package] 中未被继承的字段
为什么有害
它们可能误导使用者,让人误以为这些字段已被使用
示例
[workspace.package]
edition = "2024"
[package]
name = "foo"