【一起学Rust | 设计模式】新类型模式

简介: 【一起学Rust | 设计模式】新类型模式



前言

新的类型模式提供封装以及保证在编译时提供正确类型的值。新类型模式有多种用途和好处,比如可以处理不同的crate中的结构体和特质的关系。本期我们将一起探讨一下Rust设计模式中的新类型模式。

本期内容是学习Rust设计模式笔记


一、新类型模式

如果在某些情况下,我们希望一个类型的行为类似于另一种类型,或者在编译时强制执行某些行为,而实现这些仅使用类型别名是不够的。

例如,出于安全考虑 ,我们想要为String创建自定义实现。对于这种情况,我们可以使用该Newtype模式来提供安全类型封装

1. 新类型模式的实现

使用单个字段的元组来实现包装一个类型,使之称为一个新类型,而不是那个类型的别名,这样就可以拓展这个类型。

2. 官方例子

下面这段代码来自《Rust设计模式》,只是伪代码,并未实现具体功能,但是描述了新类型模式的思想:使用元组来包装一个新类型,通过拓展这个新类型,来拓展原本类型的功能。Foo是一个基础类型,他有他本身的实现方法,Bar包装自Foo,它除了有Foo的特性,还为其实现了新的方法,通过测试,这两种类型,已然变成了两种独立的类型。

// 一个类型,可以是自己包里面的,也可以是别的包里面的。
struct Foo {
    //..
}
impl Foo {
    // Foo 类型的实现,这些实现在Bar里面是没有的,Bar是下面的类型。
    //..
}
// 一个新类型,它包装自Foo
pub struct Bar(Foo);
impl Bar {
    // 构造函数.
    pub fn new(
        //..
    ) -> Self {
        //..
    }
    //..
}
fn main() {
    let b = Bar::new(...);
    // Foo和Bar类型不兼容,以下不进行类型检查。
    // let f: Foo = b;
    // let b: Bar = Foo { ... };
}

3. 使用动机

新类型的主要动机是抽象。它允许在精确控制接口的同时在类型之间共享实现细节。通过使用新类型而不是将实现类型作为 API 的一部分公开,它允许您向后兼容地更改实现。

这样就是区分同一个类型不同含义,就比如f64类型,可以定义成米,或者千米等不同的类型。

4. 优点

新类型是一种零成本的抽象——没有运行时开销。

新类型与包装类型是互不兼容的,因此用户不会混淆两种类型。

Rust的隐私系统确保用户不会访问到包装了的类型,他的字段默认是私有的。

5. 缺点

没有语言支持,因此包装类型的每一个方法都必须写一个传递方法,来使用包装类型的方法。并且要为每一个实现了的特质来写传递方法。这样就显得极为复杂。

二、应用

1.标识符分离

假设我们有一个用户id,他是无符号整数类型的usize。我们系统对用户操作都是通过这个用户id来实现的。就比如我们有这么一个方法:

fn get_user_id_from_username(username: &str) -> usize

如果我们还要对用户的帖子进行操作,那么代码应该这么写

let user_id: usize = get_user_id_from_username(username);
let post_id: usize = get_last_post();
fn delete_post(post_id: usize) {
  // ...
}
delete_post(user_id);

此时我们使用delete_post删除用户帖子,但是不小心传入了用户id,这就很麻烦了,这样辨识度就很不好,为了提高辨识度,我们使用新类型模式来区分两个类型:

struct UserId(pub usize);

然后让get_user_id_from_username返回该类型,

fn get_user_id_from_username(username: String) -> UserId {
  let user_id: usize = ...
  UserId(user_id)
}

这里做了如下改动

这样在我们写错代码的时候就会这么提示了

新类型模式在编译时强制执行类型安全,而在运行时没有任何性能开销。

2.为新类型添加功能

现在有如下需求,需要为用户设置个“禁止登录”的用户列表。考虑使用HashSet实现,我们定义的代码如下

let banned_users: HashSet<UserId> = HashSet::new();

但是光这一点是无法编译的,我们的UserId并没有想等,哈希等行为。我们可以使用内置派生宏来实现这些行为,会自动基于我们的结构体来生成这些实现,代码如下,

#[derive(PartialEq, Eq, Hash)]
struct UserId(usize);

3. 限制类型内容

有时候我们需要对用户名进行校验,比如我们需要用户名全部都是由小写字母组成的。现在我们来将String类型来定义成一个新类型,Username

struct Username(String);

然后创建个创建用户名的唯一方法,我们使用TryFrom特质

impl TryFrom<String> for Username {
  type Error = String;
  fn try_from(value: String) -> Result<Self, Self::Error> {
    if value.chars().all([object Object]c[object Object] matches!(c, 'a'..='z')) {
      Ok(Username(value))
    } else {
      Err(value)
    }
  }
}

这里重写了try_from行为,在类型转换时就已经对username进行了检测构造符合条件的用户名

4. 处理包之间特质和结构体的关系

在使用外部特质时我们可能会遇到以下问题

在我们的crate中使用MyTrait时,编译器就不知道我们用的是crate3中的MyTrait还是crate4中的MyTrait。Rust有一套《孤儿规则》专门来处理这种情况,我们会在后期的文章中说明。

现在我们使用新类型模式来实现外来结构体特质,或者拓展特质。

在某crate包中有如下特质

trait ToTree {
   // ...
}
fn very_useful_function(something: impl ToTree) -> () {
    // ..
}

在我们的crate中这么写

struct Wrapper(pub crate_y[object Object]MyType);
impl ToTree for Wrapper {
  // ...
}
// 使用
very_useful_function(Wrapper(foreign_value))

总结

本期介绍了Rust设计模式中的新类型模式,并且指明了该设计模式的使用场景,其优点与缺点。并且通过一个实例来应用新类型模式,拓展包装类型的行为和特质,从而实现处理包与包之间结构体和特质的关系,限制类型内容等操作。

目录
相关文章
|
20天前
|
设计模式 SQL 算法
设计模式了解哪些,模版模式
设计模式了解哪些,模版模式
21 0
|
1月前
|
设计模式 Java uml
C++设计模式之 依赖注入模式探索
C++设计模式之 依赖注入模式探索
37 0
|
3月前
|
Rust 安全 网络协议
Rust 笔记:Rust 语言中的枚举 与 模式匹配
Rust 笔记:Rust 语言中的枚举 与 模式匹配
48 0
|
2月前
|
设计模式 前端开发 JavaScript
观察者模式 vs 发布-订阅模式:两种设计模式的对决!
欢迎来到前端入门之旅!这个专栏是为那些对Web开发感兴趣、刚刚开始学习前端的读者们打造的。无论你是初学者还是有一些基础的开发者,我们都会在这里为你提供一个系统而又亲切的学习平台。我们以问答形式更新,为大家呈现精选的前端知识点和最佳实践。通过深入浅出的解释概念,并提供实际案例和练习,让你逐步建立起一个扎实的基础。无论是HTML、CSS、JavaScript还是最新的前端框架和工具,我们都将为你提供丰富的内容和实用技巧,帮助你更好地理解并运用前端开发中的各种技术。
|
16天前
|
设计模式 Java 数据库
小谈设计模式(2)—简单工厂模式
小谈设计模式(2)—简单工厂模式
|
1天前
|
设计模式 消息中间件 Java
Java 设计模式:探索发布-订阅模式的原理与应用
【4月更文挑战第27天】发布-订阅模式是一种消息传递范式,被广泛用于构建松散耦合的系统。在 Java 中,这种模式允许多个对象监听和响应感兴趣的事件。
8 2
|
4天前
|
设计模式 存储 JavaScript
[设计模式Java实现附plantuml源码~创建型] 多态工厂的实现——工厂方法模式
[设计模式Java实现附plantuml源码~创建型] 多态工厂的实现——工厂方法模式
|
4天前
|
设计模式 Java Go
[设计模式Java实现附plantuml源码~创建型] 集中式工厂的实现~简单工厂模式
[设计模式Java实现附plantuml源码~创建型] 集中式工厂的实现~简单工厂模式
|
6天前
|
设计模式
设计模式(一)简单工厂模式
设计模式(一)简单工厂模式
13 0
|
16天前
|
设计模式 Java
小谈设计模式(9)—工厂方法模式
小谈设计模式(9)—工厂方法模式