3个不稳定版本
| 0.2.0 | 2024年4月13日 |
|---|---|
| 0.1.0 | 2024年4月1日 |
| 0.1.0-pre.1 | 2023年4月29日 |
#618 in Rust模式
42 每月下载量
14KB
205 行
bty
简化Rust中品牌类型的定义和使用。
此软件包提供了Brand类型和brand!宏,可以用于在Rust中声明和无缝使用品牌类型。
[dependencies]
bty = "0.1"
支持rustc 1.60+
示例
可以使用brand!宏来声明品牌类型,这些类型将根据类型别名的名称进行区分。
bty::brand!(
type UserId = i32;
);
UserId的实例可以使用反序列化实现之一构建,例如serde或sqlx。虽然不推荐,但可以使用关联函数unchecked_from_inner手动实例化。
请参阅Matt Pocock在Twitter上发布的这个帖子,以获得更具体和直观的示例。尽管它展示了TypeScript的示例,但原理是相同的。
理由
虽然值属于同一类型,但属于不同领域的情况并不少见。例如,一个Web应用程序可以使用i32类型来表示用户ID和订单ID。
虽然这看起来合理,因为不同的域类型具有相同的类型,但很容易将一个用户ID传递给期望一个订单ID的函数。
由于Rust的类型系统是命名的,因此可以通过为每个ID引入不同的类型来避免这个问题。例如,可以这样做:
pub struct UserId(i32);
pub struct OrderId(i32);
现在编译器可以静态地确保用户ID永远不会被错误地传递给订单ID。太棒了!
尽管这种方法适用于大多数情况,但随着自定义ID类型数量的增加,它会变得难以管理,因为为了可用性的原因,类型定义本身很少足够。例如,要Clone或Debug自定义ID,必须为所有自定义类型实现这些特性。
#[derive(Clone, Debug)]
pub struct UserId(i32);
#[derive(Clone, Debug)]
pub struct OrderId(i32);
当ID类型的用途增加时,问题变得更加严重。例如,关于serde序列化和反序列化呢?
bty通过不使用品牌类型的独立类型来解决此问题。相反,使用单个Brand类型。定义为Brand<Tag, Inner>,它是一个泛型类型,具有一个Tag类型,该类型区分不同“品牌”(即领域)的值和由Inner表示的底层类型。
对于大多数Rust常用特质,如果Inner实现了它,那么Brand也会实现。这意味着如果Inner实现了Clone和Debug,那么Brand<_, Inner>也会实现它们。
按照前面的例子,可以使用bty并拥有
bty::brand!(
pub type UserId = i32;
pub type OrderId = i32;
);
i32类型并没有什么特殊之处。就像手动定义的结构体一样,任何类型都可以用来构建一个品牌类型。
许可证
MIT许可证。
依赖项
~0.1–1.8MB
~36K SLoC