默认 Cargo 特性解析器
总结
edition = "2021"
在Cargo.toml
中意味着resolver = "2"
。
详情
自 Rust 1.51.0 起,Cargo 选择性地支持 新的特性解析器,该解析器可以通过在 Cargo.toml
中设置 resolver = "2"
来激活。
从 Rust 2021 开始,这将成为默认设置。也就是说,在 Cargo.toml
中写入 edition = "2021"
将意味着 resolver = "2"
。
解析器是 工作区 的全局设置,并且该设置在依赖项中被忽略。该设置仅对工作区的顶级包有效。如果您正在使用 虚拟工作区,如果您想选择使用新的解析器,您仍然需要在 [workspace]
定义中显式设置 resolver
字段。
新的特性解析器不再合并以多种方式依赖的 crate 的所有请求特性。有关详细信息,请参阅 Rust 1.51 的公告。
迁移
没有用于更新新解析器的自动化迁移工具。对于大多数项目,更新后通常很少或没有更改。
使用 cargo fix --edition
更新时,如果新的解析器将使用不同的特性构建依赖项,Cargo 将显示一份报告。它可能看起来像这样
注意:切换到 2021 版将在 Cargo 中启用版本 2 特性解析器的使用。这可能会导致某些依赖项在构建时启用的特性比以前更少。有关解析器更改的更多信息,请访问 https://doc.rust-lang.net.cn/nightly/edition-guide/rust-2021/default-cargo-resolver.html
在构建以下依赖项时,将不再使用给定的特性bstr v0.2.16: default, lazy_static, regex-automata, unicode libz-sys v1.1.3 (as host dependency): libc
这可以让您知道某些依赖项将不再使用给定的特性构建。
构建失败
在某些情况下,您的项目在更改后可能无法正确构建。如果一个包中的依赖项声明假定另一个包中启用了某些特性,而这些特性现在被禁用了,则它可能无法编译。
例如,假设我们有这样的依赖项
# Cargo.toml
[dependencies]
bstr = { version = "0.2.16", default-features = false }
# ...
并且在我们的依赖树中的某个地方,另一个包有这样的依赖项
# Another package's Cargo.toml
[build-dependencies]
bstr = "0.2.16"
在我们的包中,我们一直在使用 bstr
中的 words_with_breaks
方法,该方法需要启用 bstr
的“unicode”特性。这在历史上是可行的,因为 Cargo 在两个包之间统一了 bstr
的特性。但是,在更新到 Rust 2021 后,新的解析器将构建两次 bstr
,一次使用默认特性(作为构建依赖项),一次不使用任何特性(作为我们的普通依赖项)。由于 bstr
现在是在没有“unicode”特性的情况下构建的,因此 words_with_breaks
方法不存在,并且构建将失败并显示该方法丢失的错误。
这里的解决方案是确保使用您实际使用的特性声明依赖项。例如
[dependencies]
bstr = { version = "0.2.16", default-features = false, features = ["unicode"] }
在某些情况下,这可能是您无法直接控制的第三方依赖项的问题。您可以考虑向该项目提交补丁,尝试为有问题的依赖项声明正确的特性集。或者,您可以从您自己的 Cargo.toml
文件中向任何依赖项添加特性。例如,如果上面给出的 bstr
示例是在某个第三方依赖项中声明的,您可以将正确的依赖项声明复制到您自己的项目中。只要它们符合新解析器的统一规则,特性就会被统一。这些规则是
- 平台特定依赖项上为当前未构建的目标启用的特性将被忽略。
- 构建依赖项和 proc-macros 不与普通依赖项共享特性。
- 开发依赖项不会激活特性,除非构建需要它们的目標(例如测试或示例)。
一个真实的例子是使用 diesel
和 diesel_migrations
。这些包提供数据库支持,并且数据库是使用特性选择的,如下所示
[dependencies]
diesel = { version = "1.4.7", features = ["postgres"] }
diesel_migrations = "1.4.0"
问题是 diesel_migrations
有一个内部 proc-macro,它本身依赖于 diesel
,并且 proc-macro 假设它自己的 diesel
副本启用了与依赖图的其余部分相同的特性。更新到新的解析器后,它无法构建,因为现在有两个 diesel
副本,并且为 proc-macro 构建的那个缺少“postgres”特性。
这里的解决方案是添加 diesel
作为构建依赖项,并使用所需的特性,例如
[build-dependencies]
diesel = { version = "1.4.7", features = ["postgres"] }
这会导致 Cargo 为主机依赖项(proc-macros 和构建依赖项)添加“postgres”作为特性。现在,diesel_migrations
proc-macro 将启用“postgres”特性,并且它将正确构建。
diesel
的 2.0 版本(目前正在开发中)没有这个问题,因为它已经过重构,不再有这个依赖项需求。
探索特性
cargo tree
命令已经进行了重大改进,以帮助迁移到新的解析器。cargo tree
可用于探索依赖图,并查看哪些特性被启用,以及重要的是_为什么_它们被启用。
一种选择是使用 --duplicates
标志(简称 -d
),它将在包被多次构建时通知您。以之前的 bstr
示例为例,我们可能会看到
> cargo tree -d
bstr v0.2.16
└── foo v0.1.0 (/MyProjects/foo)
bstr v0.2.16
[build-dependencies]
└── bar v0.1.0
└── foo v0.1.0 (/MyProjects/foo)
此输出告诉我们 bstr
被构建了两次,并显示了导致它在这两种情况下都被包含的依赖链。
您可以使用 -f
标志打印每个包正在使用的特性,如下所示
cargo tree -f '{p} {f}'
这告诉 Cargo 更改输出的“格式”,它将同时打印包和启用的特性。
您还可以使用 -e
标志来告诉它要显示哪些“边”。例如,cargo tree -e features
将在每个依赖项之间显示每个依赖项添加的特性。此选项与 -i
标志一起使用时更有用,该标志可用于“反转”树。这允许您查看特性如何_流入_给定的依赖项。例如,假设依赖图很大,并且我们不太确定谁依赖于 bstr
,以下命令将显示这一点
> cargo tree -e features -i bstr
bstr v0.2.16
├── bstr feature "default"
│ [build-dependencies]
│ └── bar v0.1.0
│ └── bar feature "default"
│ └── foo v0.1.0 (/MyProjects/foo)
├── bstr feature "lazy_static"
│ └── bstr feature "unicode"
│ └── bstr feature "default" (*)
├── bstr feature "regex-automata"
│ └── bstr feature "unicode" (*)
├── bstr feature "std"
│ └── bstr feature "default" (*)
└── bstr feature "unicode" (*)
这段输出显示项目 foo
依赖于具有“默认”特性的 bar
。然后,bar
依赖于 bstr
作为具有“默认”特性的构建依赖项。我们可以进一步看到 bstr
的“默认”特性启用了“unicode”(以及其他特性)。