03-Newtype
2 分钟阅读
译文 · 基于 Rust Design Patterns
原文链接: https://rust-unofficial.github.io/patterns/patterns/behavioural/newtype.html
Newtype
有时我们希望某个类型的行为类似于另一种类型,或在仅用类型别名不够时于编译期强制某些行为,该怎么办?
例如,出于安全考虑(如密码),我们想为 String 提供自定义的 Display 实现。
在这类情形下,可以使用 Newtype 模式来提供 类型安全 和 封装。
描述
使用只有单个字段的元组结构体,为某类型做一个不透明包装。这会创建一种新类型,而不是类型别名(type 项)。
示例
| |
| |
动机
newtype 的主要动机是抽象。它允许你在类型之间共享实现细节,同时精确控制接口。通过使用 newtype 而不是把实现类型作为 API 的一部分公开,你可以在保持向后兼容的前提下更改实现。
newtype 也可用于区分单位,例如包装 f64 以得到可区分的 Miles 和 Kilometres。
优点
被包装类型与包装类型并不类型兼容(与使用 type 相反),因此 newtype 的用户永远不会把两者「搞混」。
newtype 是零成本抽象——没有运行时开销。
隐私系统确保用户无法访问被包装类型(若字段是私有的,而默认就是私有的)。
缺点
newtype 的缺点(尤其是与类型别名相比)是没有特殊的语言支持。这意味着会有大量样板代码。你想在被包装类型上暴露的每个方法都需要一个「透传」方法,想让包装类型也实现的每个 trait 都需要一份 impl。
讨论
newtype 在 Rust 代码中非常常见。抽象或表示单位是最常见的用途,但也可用于其他原因:
- 限制功能(减少暴露的函数或实现的 trait),
- 让具有复制语义的类型具有移动语义,
- 通过提供更具体的类型来抽象,从而隐藏内部类型,例如:
pub struct Foo(Bar<T1, T2>);
这里,Bar 可能是某种公开的泛型类型,而 T1 和 T2 是某些内部类型。模块的用户不应知道我们用 Bar 来实现 Foo,但我们真正隐藏的是类型 T1 和 T2,以及它们如何与 Bar 一起使用。
参见
- 《The Book》中的高级类型
- Haskell 中的 Newtype
- 类型别名
- derive_more,一个可在 newtype 上派生许多内置 trait 的 crate。
- Rust 中的 Newtype 模式