#newtype #classification #alternative

usage

新类型模式的一个便捷替代方案

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

MIT 许可证

10KB
174

新类型模式的一个便捷替代方案

在 Rust 中构建 API 时,常见的困境是选择类型别名和新类型模式之间的选择。

考虑一个特定应用程序的 ID 集合,其底层类型为 Vec<usize>;

使用类型别名,可以选择将其定义为 pub type Identifiers = Vec<usize>,以便为类型的消费者提供对底层 Vec 方法的无限制访问,并允许 IdentifiersVec<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,并且/或者实现某些有用的访问特性,如IntoBorrowDeref

从API的角度来看,这结合了类型别名和newtype的特点:类型是独特的,但提供对其底层数据的直接访问。(但请注意,这也意味着对内部类型的更改会向外传播。)

使用目标旨在将这种子模式建模为一个通用、可重用的结构体,并提供了对标准可派生、构建和访问特性的直观实现。

实现

这是通过使用两个泛型参数实现的:类型 U 作为一个标签来标识它是一个独特类型,类型 T 用于底层数据。

U 由一个 PhantomData 表示,从而将其特质的实现与 Usage 的实现解耦。

构建和访问特性实现基于 T,允许 Usage 在尽可能多的上下文中透明地像其底层类型一样操作。

局限性

由于一致性规则,可能无法为外部类型实现外部特性。因此,在 Usage 上实现外部特性是不可行的;作为一个库类型,它设计上是外部的。

可以通过在 T 参数上实现外部特性来解决这个问题,或者使用一个实现该特性的newtype作为 T

对于在 Usage 上实现不可避免的情况,例如与某些 std 特性或常用crate中的特性兼容,请随意发送带有新功能的功能标志的pull请求,如现有的 rayonbytemuck 实现。

依赖关系

~0–300KB