塞缪尔把戏

2020-08-01 23:53:21

Semver技巧指的是将破坏性更改发布到Rust库,而不需要跨其下游依赖图进行协调升级。诀窍在于让您的库的一个版本声明对同一个库的较新版本的依赖。

铁锈图书馆生态系统曾经历过创伤性的图书馆升级。Libc从0.1升级到0.2被称为libc启示录。另一个常见的罪魁祸首是1.0版之前的Serde,从0.7到0.8再到0.9到1.0的升级需要整个生态系统的努力。

困难的原因是大量的板条箱在它们的公共API中使用这些库中的类型。

例如,假设libc机箱的简化版本只公开两个内容:c_void类型和来自NetBSD的EVFILT_AIO常量。

//libc 0.2.0 pub type c_void=/*复杂*/;pub const EVFILT_AIO:int32_t=2;

随着数百个库希望公开与C';的void*类型兼容的函数,c_void类型得到了广泛使用。同时,EVFILT_AIO常量较少使用,并且从未在下游板条箱的公共API中使用。

Extern{//Usable From C As:/void qsort(void*base,//size_t items,//size_t size,//int(*compar)(const void*,const void*));/`c_void`类型现在是该机箱的公共接口的一部分。Pub FN qort(base:*mut c_void,items:usize,size:usize,compar:option<;unsafe extern fn(*const c_void,*const c_void)->;c_int>;);}。

一段时间后,发现EVFILT_AIO应该定义为uint32_t,而不是int32_t,以匹配NetBSD头文件(rust-lang/libc#506)中其他地方使用它的方式。

此修复将是对libc机箱的突破性更改。将libc::EVFILT_AIO传递给接受int32_tw型参数的函数的现有代码将被破坏,这需要反映在libc机箱的半个版本中。

//libc 0.3.0 pub type c_void=/*复杂*/;pub const EVFILT_AIO:uint32_t=2;

尽管c_void的定义没有改变,但从技术上讲,libc 0.2中的c_void和libc 0.3中的c_void是不同的类型。在Rust中(就这一点而言,在C中一样),两个结构不能互换,因为它们看起来相同。

也就是说,如果板条箱A依赖于依赖于libc的板条箱B,而B在A调用的某个函数的公共API中使用c_void,那么在B升级到libc 0.3之前,A不能升级到libc 0.3。如果A在B之前升级,则A将尝试将libc0.3的c_void传递给B的函数,该函数仍然需要libc0.2的c_void,并且不会编译。

需要做的是,首先B升级到libc 0.3,将其作为B的主版本bump发布(因为它的公共API发生了突破性的变化),然后A可能会升级到B的新版本。

对于较长的依赖链来说,这是一个巨大的考验,需要数十个开发人员的协作防御。在最近的图书馆启示录期间,Servofound自己在三个月的时间里协调了52个图书馆的升级(伺服/伺服#8608)。

问题的核心是一个被广泛使用的API陷入了一个不那么广泛使用的API的崩溃之中。铁锈和货物能够更好地解决这一困境。

在做出重大更改并将其发布为libc 0.3.0之后,我们将发布0.2系列的一个最终次要版本,并从0.3重新导出未更改的API。

//libc 0.2.1外部板条箱libc;//根据Cargo.toml pub use libc::c_void;pub const EVFILT_AIO:int32_t=2拉入0.3;

这样,我们就避免了具有两个看起来相同但不可互换的c_void类型的问题。这里,来自libc 0.2.1的c_void和来自libc 0.3.0的c_voidfrom是完全相同的类型。

因为libc的用户可以随意地、以任何顺序从0.2升级到0.3,而不需要升级他们自己的主版本,所以避免了libcpoalyning的情况。

只要稍加小心和创新,上述技术就可以推广到多种不同的突变情况。本回购中包含的半技巧示例板条箱演示了一些可以适应的更改类型。

从根本上说,当机箱需要打破常用的API而保持广泛使用的API不变时,或者当机箱想要在其模块层次结构中调整类型时,Semver技巧是有益的。

大多数其他类型的破损都无助于此,包括下面的具体例子:

使用相同的技巧撞击不是其本身的公共依赖的主要版本,

在半小时技巧不适用的情况下,可以通过其他方式减轻破坏更改的影响。

Serde遗留填补展示了一种允许下行库跨库的多个不兼容版本同时提供特征隐含的技术。

Rust API指南的“未来校对”一章给出了一些设计API的建议,这些API从一开始就不需要破坏性更改。

就其构成可受版权保护的作品而言,依赖于同一库的未来版本的想法是根据CC01.0通用许可证(License-CC0)许可的,并且可以在没有归属的情况下使用。本文档和附带的半技巧示例机箱根据您的选择使用Apache License、版本2.0(License-Apache)或MIT License(License-MIT)进行许可。除非您另有明确说明,否则您故意提交以包含在此代码库中的任何贡献(如Apache-2.0许可证中所定义)应如上所述进行双重许可,不受任何附加条款或条件的限制。