【一起学Rust | 设计模式】新类型模式_rust 设计模式-程序员宅基地

技术标签: rust  # 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设计模式中的新类型模式,并且指明了该设计模式的使用场景,其优点与缺点。并且通过一个实例来应用新类型模式,拓展包装类型的行为和特质,从而实现处理包与包之间结构体和特质的关系,限制类型内容等操作。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_47754149/article/details/126088219

智能推荐

机器学习算法学习02:决策树的学习以及应用决策树解决Cora数据集论文分类问题_传统机器学习解决cora数据集论文分类问题-程序员宅基地

文章浏览阅读1.1k次,点赞3次,收藏7次。机器学习03:决策树的学习以及应用决策树解决Cora数据集论文分类问题文章目录机器学习03:决策树的学习以及应用决策树解决Cora数据集论文分类问题1.前言2.算法分析2.1算法概述2.2 算法优化3.算法代码3.1 决策属性优先级选择3.1.1 信息熵3.2.2 信息增益率3.3.3 基尼系数3.2 数据集的预处理3.3 决策树的生成3.4 决策树的分类4.算法运行与评估4.1 使用信息增益来划分数据集4.2 使用信息增益率划分数据4.3 使用基尼指数划分数据5.结语1.前言决策树方法作为非常经典的_传统机器学习解决cora数据集论文分类问题

NVIDIA控制面板不见了解决方法-程序员宅基地

文章浏览阅读4.4k次,点赞2次,收藏2次。NVIDIA控制面板调不出来欢迎使用Markdown编辑器你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法知识。新的改变我们对Markdown编辑器进行了一些功能拓展与语法支持,除了标准的Markdown编辑器功能,我们增加了如下几点新功能,帮助你用它写博客:全新的界面设计 ,将会带来全新的写作体验;在创作中心设置你喜爱的代码高亮样式,Markdown 将代码片显示选择的高亮

x4412开发板&ibox卡片电脑项目实战16-将hello x4412驱动编译成模块-程序员宅基地

文章浏览阅读1.3k次。在内核根目录执行make menuconfig,进入Device Drivers-> Character devices菜单界面,找到hello X4412 driver配置选项,按空格键将它配置成模块[M],保存退出。执行如下指令,保存配置好的内核配置文件:cp .config arch/arm/configs/x4412_android_defconfig 再在整个源码

新一代大数据引擎操作系统:DataWorks V2.0重磅来袭-程序员宅基地

文章浏览阅读723次。摘要: 众所周知,MaxComput与Blink分别是阿里巴巴自主研发的离线计算、实时计算大数据计算引擎,不仅拥有多项国家专利技术,而且多项关键指标已远超业内开源引擎平均能力,名副其实地成为了阿里巴巴大数据之路上的领航者。认识DataWorks:新一代大数据引擎操作系统众所周知,MaxComput与Blink分别是阿里巴巴自主研发的离线计算、实时计算大数据计算引擎,不仅拥有多项国家专利技术...

不好好作图的NCS系列(五):从这篇Cell学习GSEA的R语言分析及作图-程序员宅基地

文章浏览阅读3.6k次,点赞10次,收藏30次。之前我们在讲转录组系列的时候,说过差异基因的功能富集,用的是GO和KEGG分析。但是这远远不够,很多研究者更喜欢使用GSEA,全名是Gene Set Enrichment Analysis (基因集富集分析)。GSEA在一定程度上与GO一样,但是两者具有巨大的差别。GO使用的是差异基因,因为阈值的设定是人为的,所以很有可能遗漏一些重要基因,仅仅是因为这些基因的变化较小。而GSEA则不同,它需要的是对所有的基因进行分析,因此能够保留更多的信息。通俗的说,GSEA的适用场景是:在两种不同的生物学状态下,

Odoo2021最新实用教程之——Odoo Studio 在线开发工具实战入门指导-程序员宅基地

文章浏览阅读4.7k次。Odoo Studio 作为Odoo企业版中的核心模块,能让odoo使用者通过此设计器快速、高效地构建新模块以及方便地进行已有功能地调整。但目前缺乏与Odoo Studio有关的中文指导资料,本文通过文字与视频结合的方式讲解Odoo Studio的实战操作,希望帮助有需要的朋友快速掌握这个工具。即使你最终可能不用企业版,但通过odoo企业版在线试用提供的这个在线开发工具也能帮助你对odoo的原理更快的了解。 Odoo的在线开发工具是Odoo企业版提供的强力设计工具,本实战..._odoo studio

随便推点

【问题】解决docker 容器中文乱码_主机内的vi支持中文,但是docker内vi不支持中文-程序员宅基地

文章浏览阅读3.5k次。进入容器 查看字符集# docker exec -it <container_id> /bin/bash# locale 查看当前容器字符集# locale -a 查看容器支持的字符集从输出可以看到,系统使用的是POSIX字符集,POSIX字符集是不支持中文的,而UTF-8是支持中文的 只要把系统中的环境 LANG 改为”UTF-8”格式即可解决问题。临时设置#vi /etc/profile 进入文件加入变量# export LANG="en_US..._主机内的vi支持中文,但是docker内vi不支持中文

已知数据信息为 16位,最少应附加( )位校验位,以实现海明码纠错。_已知数据信息为16位,最少应附加-程序员宅基地

文章浏览阅读582次。已知数据信息为 16位,最少应附加( )位校验位,以实现海明码纠错。 A、3B、4C、5D、6答案:C答案解析:根据公式 2的k次方 ≥ n+k+1 , n=16 则 K=5_已知数据信息为16位,最少应附加

VUE项目报错webpack-dev-server --inline --progress --config build/webpack.dev.conf.js --host 192.168.1.10-程序员宅基地

文章浏览阅读1.1k次。首先粘上logF:\BackStage>npm run dev> [email protected] dev F:\BackStage> webpack-dev-server --inline --progress --config build/webpack.dev.conf.js --host 192.168.1.105报这个错我也是很无奈,正常情况下VU...

【ElasticSearch】RestClient和ElasticsearchTemplate实现高亮查询-程序员宅基地

文章浏览阅读1.9k次。在查询的时候希望,查询结果中的包含查询关键字高亮。(Es版本,6.5.4)前提准备,首先建立mappingPOST http://47.101.167.46:9200/hello_es/doc/_mapping{ "properties": { "name": { "type": "text", "analyzer":"ik_max_word", ...

PDF转word_pdf转word的方式csdn-程序员宅基地

文章浏览阅读2.4k次,点赞3次,收藏3次。https://www.addpdf.cn/pdf-to-word_pdf转word的方式csdn

背包问题 ——第K优解 或 次优解_次优解符号-程序员宅基地

文章浏览阅读1.9k次。附上水题一枚:http://acm.hdu.edu.cn/showproblem.php?pid=2639_次优解符号

推荐文章

热门文章

相关标签