[2023.09.18]: Rust中类型转换在错误处理中的应用解析
随着项目的进展,关于Rust的故事又翻开了新的一页,今天来到了服务器端的开发场景,发现错误处理中的错误类型转换有必要分享一下。
Rust抽象出来了Result<T,E>,T是返回值的类型,E是错误类型。只要函数的返回值的类型被定义为Resut<T,E>,那么作为开发人员就有责任来处理调用这个函数可能发生的错误。通过Result<T,E>,Rust其实给开发人员指明了一条错误处理的道路,使代码更加健壮。
场景
- 服务器端处理api请求的框架:Rocket
- 服务器端处理数据持久化的框架:tokio_postgres
在api请求的框架中,我把返回类型定义成了Result<T, rocket::response::status::Custom\<String>>
,即错误类型是rocket::response::status::Custom\<String>
。
在tokio_postgres中,直接使用tokio_postgres::error::Error
。
即如果要处理错误,就必须将tokio_postgres::error::Error
转换成rocket::response::status::Custom\<String>
。那么我们从下面的原理开始,逐一领略Rust的错误处理方式,通过对比找到最合适的方式吧。
原理
对错误的处理,Rust有3种可选的方式
- 使用match
- 使用if let
- 使用map_err
下面我结合场景,逐一演示各种方式是如何处理错误的。
下面的代码中涉及到2个模块(文件)。/src/routes/notes.rs
是路由层,负责将api请求导向合适的service。/src/services/note_books.rs
是service层,负责业务逻辑和数据持久化的处理。这里的逻辑也很简单,就是route层调用service层,将数据写入到数据库中。
使用match
src/routes/notes.rs
#[post("/api/notes", format = "application/json", data = "<note>")]
pub async fn post_notes(note: Json<Note>) -> Result<(), rocket::response::status::Custom<String>> {insert_or_update_note(¬e.into_inner()).await
}
/src/services/note_book.rs
pub async fn insert_or_update_note(note: &Note,
) -> Result<(), rocket::response::status::Custom<String>> {let (client, connection) = match connect("host=localhost dbname=notes_db user=postgres port=5432",NoTls,).await{Ok(res) => res,Err(err) => {return Err(rocket::response::status::Custom(rocket::http::Status::ExpectationFailed,format!("{}", err),));}};...match client.execute("insert into notes (id, title, content) values($1, $2, $3);",&[&get_system_seconds(), ¬e.title, ¬e.content],).await{Ok(res) => Ok(()),Err(err) => Err(rocket::response::status::Custom(rocket::http::Status::ExpectationFailed,format!("{}", err),)),}
}
通过上面的代码我们可以读出一下内容:
- 在service层定义了route层相同的错误类型
- 在service层将持久层的错误转换成了route层的错误类型
- 使用match的代码量还是比较大
使用if let
/src/services/note_book.rs
pub async fn insert_or_update_note(note: &Note,
) -> Result<(), rocket::response::status::Custom<String>> {if let Ok((client, connection)) = connect("host=localhost dbname=notes_db user=postgres port=5432",NoTls,).await{...if let Ok(res) = client.execute("insert into notes (id, title, content) values($1, $2, $3);",&[&get_system_seconds(), ¬e.title, ¬e.content],).await{Ok(())} else {Err(rocket::response::status::Custom(rocket::http::Status::ExpectationFailed,format!("{}", "unknown error"),))}} else {Err(rocket::response::status::Custom(rocket::http::Status::ExpectationFailed,format!("{}", "unknown error"),))}
}
src/routes/notes.rs
#[post("/api/notes", format = "application/json", data = "<note>")]
pub async fn post_notes(note: Json<Note>) -> Result<(), rocket::response::status::Custom<String>> {insert_or_update_note(¬e.into_inner()).await
}
使用了if let ...
,代码更加的别扭,并且在else分支中,拿不到具体的错误信息。
其实,不难看出,我们的目标是将api的请求,经过route层和service层,将数据写入到数据中。但这其中的错误处理代码的干扰就特别大,甚至要有逻辑嵌套现象。这种代码的已经离初衷比较远了,是否有更加简洁的方式,使代码能够最大限度的还原逻辑本身,把错误处理的噪音降到最低呢?
答案肯定是有的。那就是map_err
map_err
map_err是Result上的一个方法,专门用于错误的转换。下面的代码经过了map_err的改写,看上去是不是清爽了不少啊。
/src/services/note_book.rs
pub async fn insert_or_update_note(note: &Note,
) -> Result<(), rocket::response::status::Custom<String>> {let (client, connection) = connect("host=localhost dbname=notes_db user=postgres port=5432",NoTls,).await.map_err(|err| {rocket::response::status::Custom(rocket::http::Status::ExpectationFailed,format!("{}", err),)})?;...let _ = client.execute("insert into notes (id, title, content) values($1, $2, $3);",&[&get_system_seconds(), ¬e.title, ¬e.content],).await.map_err(|err| {rocket::response::status::Custom(rocket::http::Status::ExpectationFailed,format!("{}", err),)})?;Ok(())
}
src/routes/notes.rs
#[post("/api/notes", format = "application/json", data = "<note>")]
pub async fn post_notes(note: Json<Note>) -> Result<(), rocket::response::status::Custom<String>> {insert_or_update_note(¬e.into_inner()).await
}
经过map_err改写后的代码,代码的逻辑流程基本上还原了逻辑本身,但是map_err要额外占4行代码,且错误对象的初始化代码存在重复。在实际的工程项目中,service层的处理函数可能是成百上千,如果再乘以4,那多出来的代码量也不少啊,这会给后期的维护带来不小的压力。
那是否还有改进的空间呢?答案是Yes。
Rust为我们提供了From<T> trait,用于类型转换。它定义了从一种类型T到另一种类型Self的转换方法。我觉得这是Rust语言设计亮点之一。
但是,Rust有一个显示,即实现From<T> trait的结构,必须有一个在当前的crate中,也就是说我们不能直接通过From<T>来实现从tokio_postgres::error::Error
到rocket::response::status::Custom<String>
。也就是说下面的代码编译器会报错。
impl From<tokio_postgres::Error> for rocket::response::status::Custom<String> {}
报错如下:
32 | impl From<tokio_postgres::Error> for rocket::response::status::Custom<String> {}| ^^^^^---------------------------^^^^^----------------------------------------| | | || | | `rocket::response::status::Custom` is not defined in the current crate| | `tokio_postgres::Error` is not defined in the current crate| impl doesn't use only types from inside the current crate
因此,我们要定义一个类型MyError
作为中间类型来转换一下。
/src/models.rs
pub struct MyError {pub message: String,
}
impl From<tokio_postgres::Error> for MyError {fn from(err: Error) -> Self {Self {message: format!("{}", err),}}
}
impl From<MyError> for rocket::response::status::Custom<String> {fn from(val: MyError) -> Self {status::Custom(Status::ExpectationFailed, val.message)}
}
/src/services/note_book.rs
pub async fn insert_or_update_note(note: &Note,
) -> Result<(), rocket::response::status::Custom<String>> {let (client, connection) = connect("host=localhost dbname=notes_db user=postgres port=5432",NoTls,).await.map_err(MyError::from)?;...let _ = client.execute("insert into notes (id, title, content) values($1, $2, $3);",&[&get_system_seconds(), ¬e.title, ¬e.content],).await.map_err(MyError::from)?;Ok(())
}
src/routes/notes.rs
#[post("/api/notes", format = "application/json", data = "<note>")]
pub async fn post_notes(note: Json<Note>) -> Result<(), rocket::response::status::Custom<String>> {insert_or_update_note(¬e.into_inner()).await
}
而MyError
到rocket::response::status::Custom<String>
之间的转换是隐式的,由编译器来完成。因此我们的错误类型的转换最终缩短为map_err(|err|MyError::from(err))
,再简写为map_err(MyError::from)
。
关于错误处理中的类型转换应用解析就到这里。通过分析这个过程,我们可以看到,在设计模块时,我们应该确定一种错误类型,就像tokio_postgres库一样,只暴露了tokio_postgress::error::Error一种错误类型。这种设计既方便我们在设计模块时处理错误转换,也方便其我们的模块在被调用时,其它代码进行错误处理。
相关文章:
![](https://www.ngui.cc/images/no-images.jpg)
[2023.09.18]: Rust中类型转换在错误处理中的应用解析
随着项目的进展,关于Rust的故事又翻开了新的一页,今天来到了服务器端的开发场景,发现错误处理中的错误类型转换有必要分享一下。 Rust抽象出来了Result<T,E>,T是返回值的类型,E是错误类型。只要函数的返回值的类…...
![](https://www.ngui.cc/images/no-images.jpg)
前端工作日常
机缘 记录和遇到的问题作为记录 收获 收获代码提高和认知 日常 使用js去操作数组或者对象 空闲时间可以多学学基础算法 比如(冒泡,倒序,去重,笛卡尔积算法,各种各样的排序方法等等等) 正确良好的使用循环…...
![](https://www.ngui.cc/images/no-images.jpg)
C++:C++哪些时候用到const
声明常量:使用const关键字定义一个常量,不允许对其进行更改。例如: const int PI 3.1415926;修饰函数参数:加上const限定符可以确保函数不会修改传入的参数值。例如: void print(const int num) {// num不能在函数内…...
![](https://img-blog.csdnimg.cn/78dfafca87a94d3daa78f41fa0daa7f2.png)
OpenCV之九宫格图像
将一张图像均等分成九份,然后将这九个小块按一定间隔(九宫格效果)拷贝到新画布上。效果如下图所示: 源码: #include<iostream> #include<opencv2/opencv.hpp> using namespace std; using namespace cv;i…...
![](https://img-blog.csdnimg.cn/8f4ac69a5a5f485d919fa6ecf43c3af0.jpeg)
OpenGLES:绘制一个颜色渐变的圆
一.概述 今天使用OpenGLES实现一个圆心是玫红色,向圆周渐变成蓝色的圆。 本篇博文的内容也是后续绘制3D图形的基础。 实现过程中,需要重点关注的点是:如何使用数学公式求得图形的顶点,以及加载颜色值。 废话不多说,…...
![](https://img-blog.csdnimg.cn/753ccc3812bb4fc8b3770f9986c68802.png)
javascript数据类型错误造成的前端分页不准的问题
有个react项目是自己写的mock后端api,使用的是json文件模拟DB, slice函数模拟分页,但是在实际分页时,发现了分页不准的问题,现象如下: 当pageSize为5的时候(共16条数据),总共分4页&…...
![](https://img-blog.csdnimg.cn/7da4d73e8a8b419fbe11a1b129421079.png#pic_center)
[Qt]QListView 重绘实例之二:列表项覆盖的问题处理
0 环境 Windows 11Qt 5.15.2 MinGW x64 1 系列文章 简介:本系列文章,是以纯代码方式实现 Qt 控件的重构,尽量不使用 Qss 方式。 《[Qt]QListView 重绘实例之一:背景重绘》 《[Qt]QListView 重绘实例之二:列表项覆…...
![](https://img-blog.csdnimg.cn/14255831bb8f465f8107411ed87f7534.png)
Java 函数式编程思考 —— 授人以渔
引言 最近在使用函数式编程时,突然有了一点心得体会,简单说,用好了函数式编程,可以极大的实现方法调用的解耦,业务逻辑高度内聚,同时减少不必要的分支语句(if-else)。 一、函数式编…...
![](https://img-blog.csdnimg.cn/a64aeb4eeae1415c8addd8a697e8393c.png)
操作系统权限提升(二十八)之数据库提权-SQL Server 数据库安装
SQL Server 数据库安装 SQL Server介绍 SQL Server 是Microsoft 公司推出的关系型数据库管理系统。具有使用方便可伸缩性好与相关软件集成程度高等优点,可跨越从运行Microsoft Windows 98 的膝上型电脑到运行Microsoft Windows 2012 的大型多处理器的服务器等多种平台使用。…...
![](https://www.ngui.cc/images/no-images.jpg)
腾讯mini项目-【指标监控服务重构-会议记录】2023-08-18
2023-08-18 会议纪要 进度 venus 的 metrics 独立分支开发venus 的 trace 修复了一些bug 返回 error 主动调用 span.end() profile 的 watemill pub/sub 和 trace 上报还原原本功能profile 的 hyperscan 的继续调研 待办 调研如何关闭otel,设置开关配置性能benc…...
![](https://www.ngui.cc/images/no-images.jpg)
如何通过axios拦截器,给除了登录请求以外,axios的所有异步请求添加JWT令牌!
在 Vue 项目中配置除了登录请求以外的所有请求的令牌,通常涉及到在请求头中添加令牌(Token)信息。这可以通过使用 Axios 或其他 HTTP 请求库来实现。以下是一般的步骤: 1. **安装 Axios**: 如果你还没有安装 Axios&a…...
![](https://img-blog.csdnimg.cn/042c843b7f2f4ab1bcccaf498c38fbb6.png)
Spring学习笔记9 SpringIOC注解式开发
Spring学习笔记8 Bean的循环依赖问题_biubiubiu0706的博客-CSDN博客 注解的存在主要是为了简化XML的配置.Spring6倡导全注解式开发 回顾下 注解怎么定义,注解中的属性怎么定义 注解怎么使用 通过反射机制怎么读取注解 注解的自定义 注解的使用 通过反射机制怎么读取注解 I…...
![](https://www.ngui.cc/images/no-images.jpg)
【新日标习题集】第13課 までのまとめ (discarded)
2. 学校にコンピューターがごだいあります。 这个句子好像有点问题,辞典中没有查到有「ごだい」这个单词 学校里有5台电脑。 5. わたしは英語がよくわかります。 我很懂英语。...
![](https://img-blog.csdnimg.cn/d52631c851c04f4eafad7240cc93eb16.png)
Java基础常考知识点(基础、集合、异常、JVM)
作者:逍遥Sean 简介:一个主修Java的Web网站\游戏服务器后端开发者 主页:https://blog.csdn.net/Ureliable 觉得博主文章不错的话,可以三连支持一下~ 如有需要我的支持,请私信或评论留言! Java基础常考知识点…...
![](https://img-blog.csdnimg.cn/c30c94a3472745dabc3760c083398e73.jpeg)
虚拟机桥接模式下没有无线网卡选项
我以为是雷电模拟器占用了网卡的缘故,但想起之前可能修改了无线网卡的某些内容,于是到网络属性里面查看。 如下所示,原来是之前我不小心把这个红箭头指向的项目取消勾选了。...
![](./picture/17.visitor.png)
设计模式笔记
关于设计模式 1. 如何阅读本文 略 2. 面向对象程序设计简介 2.1 面向对象程序设计基础 面向对象程序设计 (Object-Oriented Programming,缩写为 OOP)是一种范式,其基本理念是将 数据块 及 与数据相关的行为 封装成为特殊的、…...
![](https://img-blog.csdnimg.cn/4fd10ff78133478bb7d1b75ac37602d9.png)
c==ubuntu+vscode debug redis7源码
新建.vscode文件夹,创建launch.json和tasks.json {"version": "0.2.0","configurations": [{"name": "C/C Launch","type": "cppdbg","request": "launch","prog…...
![](https://www.ngui.cc/images/no-images.jpg)
java字符串储存底层原理
字符串原理:原理1: 内存原理 (1)直接赋值给字符串,会把这个字符串放到常量池里,如果之后出现重复使用这个字符串的,就会直接从这个常量池中去引用,不会再去new一个字符串 (2)new出来的字符串不会重复使用,而是开辟一个新的空间存储原理2: 字符串中的""比较的是什么?…...
![](https://img-blog.csdnimg.cn/d62247cba2b34c88b518064e8f163bfe.png)
c++获取当前时间的字符串
代码 void getNowTimePrefix(std::string& prefix) {std::time_t nowTime;struct tm* p new tm;std::time(&nowTime);localtime_s(p, &nowTime);int year p->tm_year 1900;int month p->tm_mon 1;int day p->tm_mday;int hour p->tm_hour;int …...
![](https://www.ngui.cc/images/no-images.jpg)
【精品】通用Mapper 批量更新bug解决方案
问题描述 环境:mysql8.xmybatis3.5.13tk.mybatis4.2.3 在使用tk.mybatis做批量更新时,程序会报错,说是执行的SQL语法错误,经研究源代码发现tk.mybatis在实现批量更新时是通过多次执行update语句实现的。这本身就不符合MySQL批量…...
![](https://img-blog.csdnimg.cn/img_convert/e0fe9c14d11c98856f367dd9fc3caf95.png)
腾讯mini项目-【指标监控服务重构-会议记录】2023-07-06
7/6 会议记录 Profile4个步骤 解压kafka消息初始化性能事件,分析事件将数据写入kafkaRun 开始执行各stage handler 上报耗时到otel-collector。。。 // ConsumerDispatchHandler consumer // // param msg *sarama.ConsumerMessage // param consumer *databus.K…...
![](https://www.ngui.cc/images/no-images.jpg)
【React】函数式组件和类式组件的用法和逻辑
组件的使用 当应用是以多组件的方式实现,这个应用就是一个组件化的应用 注意: 组件名必须是首字母大写虚拟DOM元素只能有一个根元素虚拟DOM元素必须有结束标签 < /> 渲染类组件标签的基本流程React 内部会创建组件实例对象调用render()得到虚拟 …...
![](https://www.ngui.cc/images/no-images.jpg)
题目 1061: 二级C语言-计负均正
从键盘输入任意20个整型数,统计其中的负数个数并求所有正数的平均值。 保留两位小数 样例输入 1 2 3 4 5 6 7 8 9 10 -1 -2 -3 -4 -5 -6 -7 -8 -9 -10 样例输出 10 5.50 解题思路: 如题所示,输入20个正负数,---》求付数的个…...
![](https://www.ngui.cc/images/no-images.jpg)
数位和(C++)
系列文章目录 进阶的卡莎C++_睡觉觉觉得的博客-CSDN博客数1的个数_睡觉觉觉得的博客-CSDN博客双精度浮点数的输入输出_睡觉觉觉得的博客-CSDN博客足球联赛积分_睡觉觉觉得的博客-CSDN博客大减价(一级)_睡觉觉觉得的博客-CSDN博客小写字母的判断_睡觉觉觉得的博客-CSDN博客纸币(…...
![](https://img-blog.csdnimg.cn/fb96e6a97ebe4f428df1e485904e9a62.png)
[牛客复盘] 牛客周赛round13 20230924
[牛客复盘] 牛客周赛round13 20230924 总结矩阵转置置2. 思路分析3. 代码实现 小红买基金1. 题目描述2. 思路分析3. 代码实现 小红的密码修改1. 题目描述2. 思路分析3. 代码实现 小红的转账设置方式1. 题目描述2. 思路分析3. 代码实现 小红打boss1. 题目描述2. 思路分析3. 代码…...
![](https://img-blog.csdnimg.cn/1df7c30a92db4dc68b77bf679a1e8eab.png)
mybatsi-MyBatis的逆向工程
mybatsi-MyBatis的逆向工程 一、前言二、创建逆向工程的步骤1.添加依赖和插件2.创建MyBatis的核心配置文件3.创建逆向工程的配置文件4.执行MBG插件的generate目标 一、前言 正向工程:先创建Java实体类,由框架负责根据实体类生成数据库表。 Hibernate是支…...
![](https://img-blog.csdnimg.cn/175095f40ea04403a8744034fa35bd1f.jpeg)
转转闲鱼交易猫链接源码 支持二维码收款
最新仿二手闲置链接源码 后台一键生成链接,后台管理教程:解压源码,修改数据库config/Congig 不会可以看源码里有教程 下载程序:https://pan.baidu.com/s/16lN3gvRIZm7pqhvVMYYecQ?pwd6zw3...
![](https://img-blog.csdnimg.cn/c72789ccf98543429cfce933a34bd293.png)
Python爬虫基础(三):使用Selenium动态加载网页
文章目录 系列文章索引一、Selenium简介1、什么是selenium?2、为什么使用selenium3、安装selenium(1)谷歌浏览器驱动下载安装(2)安装selenium 二、Selenium使用1、简单使用2、元素定位3、获取元素信息4、交互 三、Phan…...
![](https://img-blog.csdnimg.cn/58cd8b645b214c1d83e276d191768287.png)
Linux系统下安装Mysql
1、执行命令:rpm -qa | grep -i mysql,先查看系统之前是否有安装相关的rpm包,如果有,会显示类似下面的信息; 2、通过命令yum -y remove mysql-* 一次性删除系统上所有相关的rpm包,或者通过命令yum -y …...
![](https://img-blog.csdnimg.cn/55cb22d4ac11458d9ec2f7a07fc9cb0f.png)
Jenkins学习笔记1
CI 服务器: 认识Jenkins: Jenkins是一个可扩展的持续集成(CI)引擎,是一个开源项目,旨在提供一个开放易用的软件平台,使得软件持续集成变成可能。Jenkins非常易于安装和配置,简单易…...
![](https://img-blog.csdnimg.cn/2d1801fddb834ef58ebaae13551b4012.png)
社交类网站手机模版/软文营销的概念
目录 1、什么是自动化测试 2、自动化测试的发展前景怎么样 3、自动化测试难不难? 4、目前市场上自动化测试岗位的薪资是多少? 5、自动化测试学习方法好渠道 6、自动化测试怎么学? 学习基础知识 选择自动化测试框架 开始编写测试脚本 …...
![](/images/no-images.jpg)
网站建设做的人多吗/优化大师网页版
问题 C: 八皇后 时间限制: 1 Sec 内存限制: 128 MB 提交: 54 解决: 48 [提交][状态][讨论版][命题人:quanxing] 题目描述 会下国际象棋的人都很清楚:皇后可以在横、竖、斜线上不限步数地吃掉其他棋子。如何将8个皇后放在棋盘上(有8 8个方格&#x…...
![](https://img-my.csdn.net/uploads/201205/05/1336185963_4905.jpg)
镇江网站建设远航科技/市场策划方案
NUMA与SMP SMP(Symmetric Multi-Processor),即对称多处理器结构,指服务器中多个CPU对称工作,每个CPU访问内存地址所需时间相同。其主要特征是共享,包含对CPU,内存,I/O等进行共享。SMP的优点是能够保证内存一…...
![](/images/no-images.jpg)
做平面vi网站/个人在线网站推广
目录前言NumPy一、NumPy库引用二、N维数组对象:ndarraynp.array()生成一个ndarray数组,np.array()输出成[]形式,元素由空格分割ndarray对象的属性ndarray数组的元素类型ndarray数组的创建ndarray数组的变换ndarray数组的操作1. 索引与切片2. 运算三、Num…...
音乐网站设计外国/seo营销怎么做
Go Web编程--SecureCookie实现客户端Session管理在Web应用开发中Session是在用户和服务器之间进行交换的非持久化交互信息。当用户登录时,可以在用户和服务器之间生成Session,然后来回交换数据,并在用户登出时销毁Session。gorilla/sessions软…...
![](/images/no-images.jpg)
购物网站开发/项目网站
(尊重劳动成果,转载请注明出处:http://blog.csdn.net/qq_25827845/article/details/73565502冷血之心的博客) 剑指Offer(第二版)面试题34:二叉树中和为某一个值的路径 题目:二叉树中…...