Rust Cargo依赖包的版本(语义化版本控制)教程与 TryV2 的引入

指定的Crate版本与编译时的Crate版本不一致

有时使用Rust时,我们会发现原本可以编译的文件无法编译。我们举一个例子来说明这个问题。

如果你在2021年5月19日之后编译不包含Cargo.lock文件的某个使用了Rocket(一个Rust编写的Web框架)0.4.6版本的项目,你可能会遇到一些让你感到困惑的问题:

  1. 如果你将rust编译器的版本升级到了rustc 1.54.0-nightly (4e3e6db01 2021-05-18)或者之后某几个版本的nightly,然后仍在Cargo.toml文件中以Rocket = "0.4.7"的方式引入0.4.7及以前的Rocket版本作为依赖。你就会发现编译器编译的实际上是0.4.8或之后的Rocket版本;
  2. 如果你没有升级到上述版本的rustc/cargo,仍然用上述方式引入Rocket,会发现原本可以编译的Cargo项目编译报错,要求必须要升级编译器;
  3. 如果你升级到了上述nightly版本的编译器,然后以Rocket = "=0.4.7"(第二个等号是必要的)的方式引入0.4.7及以前的Rocket版本作为依赖,你会发现编译错误。

如果你对语义化版本控制、Rust以及Cargo不够了解,你可能会在此时质问:为什么曾经能够编译的版本现在却会引发那么多乱七八糟的编译错误呢?!

这事涉及到的内容还不少,所以我们需要下详细来说一说。

语义化版本 Semantic Versioning

Cargo中的依赖包通过一个叫做Semver的包来实现版本号的对应关系。这个包主要的功能就是实现语义化版本的版本号控制

关于语义化版本的详细信息,我真诚推荐各位阅读https://semver.org/lang/zh-CN/以了解详细的信息。不过我们可以将它稍微简化一下,使得各位可以更简单的理解语义化版本的规则。

我制作了这样一个流程图供参考用,它较为形象的把Cargo判断依赖包版本的方式画了出来。

Cargo处理依赖包的语义化版本号的流程图

不过如果你觉得这样理解比较困难,我们还可以举几个例子来说明一下它的运作方式:

# 加入^符号或者不加任何符号

^0.2.3  :=  >=0.2.3, <0.3.0
^0.2    :=  >=0.2.0, <0.3.0
^0.0.3  :=  >=0.0.3, <0.0.4
^0.0    :=  >=0.0.0, <0.1.0
^0      :=  >=0.0.0, <1.0.0
^1.2.3  :=  >=1.2.3, <2.0.0
^1.2    :=  >=1.2.0, <2.0.0
^1      :=  >=1.0.0, <2.0.0

# 对于主版本大于等于1的
# 可以通过波浪号要求与主版本为0时相同的策略

~1.2.3  := >=1.2.3, <1.3.0
~1.2    := >=1.2.0, <1.3.0
~1      := >=1.0.0, <2.0.0

# 还可以使用通配符

*     := >=0.0.0
1.*   := >=1.0.0, <2.0.0
1.3.* := >=1.3.0, <1.4.0

# 可以使用比较运算符

>= 1.3.0
> 2
< 3
= 1.3.4

# 当然也可以使用通过逗号组合的方式来指定版本

更详细的信息推荐阅读Cargo的官方文档Specifying Dependencies

Rust Nightly 与 Cargo Dependencies

我们回到开头的问题。也许很多新手会疑惑,为什么会有这么奇怪的问题发生呢?我们首先阅读在GitHub中有关Rocket的几个issue:

从上述issue中不难发现问题的根源出在rustc 1.54.0-nightly (4e3e6db01 2021-05-18)这个nightly版本中。在这个编译器版本中,测试性地引入了 std::ops::TryV2 这一特性。这个特性是对于现有 std::ops::Try 特性的一次改造。在RFC #1859中,早期的Try特性被引入,其核心可以概述为引入了 ? 符号来替代 try_opt!() 宏的功能。但是,这一特性时至今日仍不能算作是一个稳定的特性。为了对RFC #1859的目的的进一步发扬光大,同时提升Try特性的效率,完善这一特性,Rust开发团队于RFC #3058中提出了 TryV2 特性,并最终在上述nightly编译器中首次测试性地应用了这一特性。而由于此前的 Try trait 就被广泛使用,try_trait_v2 的到来直接导致了大量依赖文件需要进行更新。这也是为什么Rocket的作者Sergio Benitez这个几个月不发一次新版本、0.5已经🕊鸽到猴年马月了的人,在上述nightly版本发布后的一天内连续发布了0.4.8和0.4.9两个新版本。

如果想要了解有关 TryV2 的相关信息,欢迎阅读以下内容:

解决方案:由于Rust官方强制遵守语义化版本的要求,大部分crate会严格遵循语义化版本的开发规则。因此,对于任何依赖包,最佳方式就是使用^或~或不使用符号,这样既可以保持依赖的API稳定,又可以无损伤修复bug,一举多得。

《Rust Cargo依赖包的版本(语义化版本控制)教程与 TryV2 的引入》有1条评论

发表评论