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