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