5 个稳定版本
| 1.4.0 | 2022 年 1 月 16 日 |
|---|---|
| 1.3.0 | 2022 年 1 月 15 日 |
| 1.2.0 | 2022 年 1 月 15 日 |
| 1.1.0 | 2022 年 1 月 15 日 |
| 1.0.0 | 2022 年 1 月 15 日 |
#1184 在 数据结构 中
用于 shambler
10KB
174 行
新类型模式的一个便捷替代方案
在 Rust 中构建 API 时,常见的困境是选择类型别名和新类型模式之间的选择。
考虑一个特定应用程序的 ID 集合,其底层类型为 Vec<usize>;
使用类型别名,可以选择将其定义为 pub type Identifiers = Vec<usize>,以便为类型的消费者提供对底层 Vec 方法的无限制访问,并允许 Identifiers 与 Vec<usize> 互换使用。
相反,也可以通过新类型来定义它为 pub struct Identifiers(Vec<usize>)。这创建了一个与 Vec<usize> 语义上不同的类型,但隐藏了对其底层方法的访问。
创建这样的独立类型是 Rust 类型系统的一个优势,因为它允许在类型时间而不是运行时编码数据依赖关系
pub struct Identifiers(Vec<usize>);
pub fn create_ids() -> Identifiers {
Identifiers(vec![0, 1, 2, 3])
}
pub fn munge_ids(ids: Identifiers) {
// ...
}
// Valid
let ids: Identifiers = create_ids(); // Known-correct IDs provided by a trusted function
munge_ids(ids);
// Not valid
// let ids = vec![999, 6, 876]; // IDs created arbitrarily, no guarantee of correctness
// munge_ids(ids); // Compiler error, incorrect type
在某些情况下,隐藏对底层类型方法的访问可能是可取的,因为它允许通过 API 确定可用的功能,从而隐式地向库消费者提供有关其使用的信息。
但这并不适用于所有情况。例如,像 Vec 这样的集合类型;它们有如此多的有用方法和特质实现,手动在新类型上重新公开每个方法变得不切实际,可能结果是一个过于严格的架构。
在这些情况下,内部类型可以被标记为 pub,并且/或者实现某些有用的访问特性,如Into,Borrow和Deref。
从API的角度来看,这结合了类型别名和newtype的特点:类型是独特的,但提供对其底层数据的直接访问。(但请注意,这也意味着对内部类型的更改会向外传播。)
使用目标旨在将这种子模式建模为一个通用、可重用的结构体,并提供了对标准可派生、构建和访问特性的直观实现。
实现
这是通过使用两个泛型参数实现的:类型 U 作为一个标签来标识它是一个独特类型,类型 T 用于底层数据。
U 由一个 PhantomData 表示,从而将其特质的实现与 Usage 的实现解耦。
构建和访问特性实现基于 T,允许 Usage 在尽可能多的上下文中透明地像其底层类型一样操作。
局限性
由于一致性规则,可能无法为外部类型实现外部特性。因此,在 Usage 上实现外部特性是不可行的;作为一个库类型,它设计上是外部的。
可以通过在 T 参数上实现外部特性来解决这个问题,或者使用一个实现该特性的newtype作为 T。
对于在 Usage 上实现不可避免的情况,例如与某些 std 特性或常用crate中的特性兼容,请随意发送带有新功能的功能标志的pull请求,如现有的 rayon 和 bytemuck 实现。
依赖关系
~0–300KB