AD19 add pins to nets错误_为什么我认为Rust的Result错误处理方式不如Exception

61eed7b15f67f39343ca1892535e298f.png

由于是对技术的个人评判,欢迎理性讨论。

我曾经也当过纯函数式的脑残粉,认为宇宙第一棒的代数数据结构用来处理错误,是无上的优雅和绝对的安全。一个看似人畜无害的接口抛出异常带来的崩溃,是各类疑难杂症的罪魁祸首。综合起来,sum 类型相比 exception 的优势有:

  • 不可变数据结构,purity is dignity;
  • 编译检查,必须处理,懒鬼退散,nobody can avoid it(异常经常不被妥善处理,直至发酵)
  • 可以写在接口声明中,并且严格规定了能产生的错误类型,anyone can hear the lion in the box(几乎所有使用异常机制的语言都不要求标注异常,且一个函数可能抛出任何预料之外类型的异常);
  • 恢复错误时没有exception那么大的开销。可以用清晰简洁的方法处理结果(unwrap, expect 等临时方法,以及 and, or 等 combinator),并且能清楚地区分对待严重故障(panic)和预料之内的小问题(相对而言,捕捉异常要写大堆 try ... catch 方块,并且 exception 和 panic 一样直接崩溃)

然而我越去实际使用这两种方法,越发觉得 exception 要远好于 sum type。原因如下:

  • 不强制要求处理异常,不是异常机制的问题,而是编译器设计的问题。编译器可以要求抛出异常的函数标注其抛出的异常类型,且调用这种函数的函数如果不处理这些异常,也必须标注这些异常的类型;
  • 如果强制处理异常,程序员就会抱怨 try ... catch 太多太烦。但这个麻烦也完全是设计问题。学习 Rust,同样可以设计?运算符,用例如 foo()?,在 foo() 未抛出异常时直接使用结果,否则抛出异常给上层。unwrap, except之类的操作符也很容易实现,combinator 也不在话下;
  • 代码量大了,到处都是 Result<T, E> 很让人崩溃。很多很简单的功能因为要适应其内部调用的函数或外部调用它的函数,也不得不给返回类型加上 Result。虽然为了安全,这是必要的开销,但是这里面暗藏两个问题:
    • 第1个,多写那么多 Result,并不能在错误出现时让我获得更多。层层传递的 Result不会自动保存调用链条,无法像 exception 那样从最深处 propogate(浮现?),保存直达病灶的堆栈信息。所以到需要输出问题的时候,Result 只有一行,exception 有几百行(或许编译器也可以给Result做优化);
    • 第2个,Result 的 err type 实际上自缚手脚,把函数里可能产生的错误类型勒死在 err type 上。有时函数可能产生多种错误,却非要用一个单独的错误来统一,而这个单独错误还得起一个面面俱到的名字,最后名字变得极为抽象,不明所以,最后就统一一种了事(虽然经常是工程设计问题,但很多时候身不由己)。相比而言,抛出何种 exception 完全由各个功能模块自己决定,不需要相互约束。IO 产生的错误到外面还是 IOError,没有转换的开销。
  • 最后一点,用了 Result,函数签名不再纯净,我的工厂生产的啤酒不再是啤酒,而是Result<啤酒, 生产错误>,做出来的菜不再是菜,而是 Result<菜, 失败料理>再也不能挥挥洒洒写逻辑,思考也受处理 Result 阻碍。

所以我的提法是,不要抛弃异常。甚至,在有方便的异常处理操作符,且编译器严格要求程序去处理之时,可以完全不需要 Result。作为例子,看以下 Rust 代码,它是一个语法分析程序:

enum Token {/* 定义词法分析的结果:token */
}enum Expr {/* 定义语法分析的结果:表达式 */
}/* 包含多种错误,但严格来说 EOF 并不算词法错误 */
enum LexError {EOF,UnexpectedEOF,Lexeme(String),
}/* 从源码获取下一个 token */
fn next_token(source: &str) -> Result<Token, LexError> { /* ... */ }/* 不得不将 LexError 整合进来 */
enum ParseError {EOF,UnexpectedEOF,Syntax(String),
}/* 从源码解析一个表达式,看起来还挺优雅,但有转换开销 */
fn parse(source: &str) -> Result<Expr, ParseError> {/* ... */match next_token(source) {Ok(token) => /* ... */,Err(LexError::EOF) => Err(ParseError::EOF),Err(LexError::UnexpectedEOF) => Err(ParseError::UnexpectedEOF),Err(LexError::Lexeme(msg)) => Err(ParseError::Syntax(msg)),}
}/* 事情刚开始麻烦起来 */
enum ParseFileError {Syntax(ParseError),IO(IOError),
}/* 从源文件路径读取源码并解析成表达式,混入了 io::Error,可以看到判断逻辑已十分复杂,很多时候程序员都直接 unwrap 了事 */
fn parseFile(path: &str) -> Result<Expr, ParseFileError> {let mut file = std::fs::File::open(path);if let Err(ioError) = file {return ParseFileError::IO(ioError);}let mut source = String::new();if let Err(ioError) = file.read_to_string(&mut contents) {return ParseFileError::IO(ioError);}let expr = parse(&source[..]);if let Err(parseError) = expr {return ParseFileError::Syntax(parseError);}return expr.unwrap();
}

再来看有严格的 exception 会怎样:

enum Token {/* 定义词法分析的结果:token */
}enum Expr {/* 定义语法分析的结果:表达式 */
}/* 可以任意定义多种异常 */
#[derive(Exception)]
struct EOF;#[derive(Exception)]
struct UnexpectedEOF;#[derive(Exception)]
struct LexError(String);#[derive(Exception)]
struct SyntaxError(String);/* 返回类型变为单纯的 Token,加上异常标注 */
fn next_token(source: &str) -> Tokenthrows EOF, UnexpectedEOF, LexError { /* ... */ }/* 返回类型变为单纯的 Expr,并且只需处理有必要处理的 next_token 抛出的异常 */
fn parse(source: &str) -> Exprthrows EOF, UnexpectedEOF, SyntaxError {/* ... */let token = next_token(source) except {LexError(msg) => throw SyntaxError(msg),_ => throw _,}
}/* io::Error 除了要标注,可以完全不用管,清爽很多。再见了,unwrap! */
fn parseFile(path: &str) -> Exprthrows EOF, UnexpectedEOF, SyntaxError, io::Error {let mut file = std::fs::File::open(path)?;let mut source = String::new();file.read_to_string(&mut contents)?;let expr = parse(&source[..])?;return expr;
}

意义不言自明。

更新,感谢评论区 lanus 大佬(不知道为什么@不到)的启发,补充两点。

  1. 足够强大的编译器可以推断 Exception,而不需要手动用 throws 标记。相反,用 nothrow 标记不希望抛异常的函数,在里面编译器强制要求捕获并处理所有异常。
  2. Result 即便要转换,但开销还是少于恢复 exception。我想了一下,解决方向有两种,一种是仍然不要 Result,设法降低 exception 开销;另一种是加入 Result,但不用标注,由编译器推断,保留两种开销不同的机制。后者看起来更完美,但编译器要暗戳戳地改变返回类型。并且必须有匿名枚举,就像 TypeScript 的那种纯粹的 sum type。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/539498.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java @Transient 注解使用

2019独角兽企业重金招聘Python工程师标准>>> 我们建实体类的时候啊&#xff0c;有时候实体类的属性和数据库表字段不一致的时候&#xff0c;比如多一个属性&#xff0c;那你不加这个注解就会报错&#xff0c;因为映射的时候会提示&#xff08;提示啥我给忘了&#x…

oracle—ebs_采购功能点操作手册,oracle—EBS_采购功能点操作手册

erp实施企业采购模块构架及日常业务操作指导“人力资源”页签可以设置是否“使用审批层次结构”来决定采购单据的审批路径。如不选定&#xff0c;则表示采购单据将使用员工的“主管”结构来进行审批。“员工编号”方法默认自业务组定义时的“员工编号生成”方法设置。1.2采购选…

combobox控件 如何把三角形放大_初中数学|全等三角形全部知识点总结

今天&#xff0c;琦老师为大家整理了[初中数学重要考点&#xff0c;全等三角形的全部知识点]&#xff0c;希望帮助大家中考数学快速提分( #小学数学#初中数学#数学)除了图片内容&#xff0c;大米君也为大家分享[如何灵活运用这些知识点的方法]大家记得跟着做起来哦~提高复习效果…

Spring中引入其他配置文件

原文&#xff1a;http://www.cnblogs.com/LiuChunfu/p/5605473.html ------------------------------------------------------------------------------ 一、引入其他 模块XML   在Spring的配置文件&#xff0c;有时候为了分模块的更加清晰的进行相关实体类的配置。 比如…

oracle不要重复记录,Oracle中去重复记录 不用distinct

用distinct关键字只能过滤查询字段中所有记录相同的(记录集相同)&#xff0c;而如果要指定一个字段却没有效果&#xff0c;另外distinct关键字会排序&#xff0c;效率很低 。select distinct name from t1 能消除重复记录&#xff0c;但只能取一个字段&#xff0c;现在要同时取…

miui游戏驱动程序偏好设置_米粉必看:小米官方教你如何关闭 MIUI 广告 - 小米,MIUI...

IT之家 11 月 9 日消息 小米的 MIUI 在用户心中的口碑一直是比较好的&#xff0c;但该系统也并非完美&#xff0c;被吐槽的较多的一个点就是系统内的广告比较多。今天&#xff0c;小米官方微博 小米服务那些事 分享了几个能够减少 MIUI 广告的方法&#xff0c;米粉不妨了解一下…

Spring Cloud Zuul网关 Filter、熔断、重试、高可用的使用方式。

时间过的很快&#xff0c;写springcloud(十)&#xff1a;服务网关zuul初级篇还在半年前&#xff0c;现在已经是2018年了&#xff0c;我们继续探讨Zuul更高级的使用方式。 上篇文章主要介绍了Zuul网关使用模式&#xff0c;以及自动转发机制&#xff0c;但其实Zuul还有更多的应用…

oracle 9 插入日期,oracle date日期类型 精析

一、date1.date、sysdate格式说明展示date类型&#xff0c;展示格式既可以为&#xff1a;YYYY/MM/DD&#xff0c;也可以为YYYY/MM/DD HH24:MI:SS&#xff1b;其存储格式只有一种&#xff1a;YYYY/MM/DD HH24:MI:SS展示格式一&#xff1a;当你只存年月日时&#xff0c;date实际存…

CentOS 7.1下KVM的安装与配置

由于没有物理机可用&#xff0c;在自己的VMware Workation中CentOS 7搭建完成。 首先查看VMware Workation是否支持虚拟化&#xff0c;把红框内打钩即可。 虚拟化开启并安装CentOS系统&#xff0c;建议CentOS安装64bit。我的环境用的是CentOS 7。进入系统&#xff0c;首先查看服…

软件生成问候图片_这些社交软件你玩过几个?

提到聊天软件&#xff0c;我们的手机肯定安装有微信QQ&#xff0c;不过微信QQ都是主打熟人社交&#xff0c;而下面几个软件都是陌生人社交。这些社交软件&#xff0c;总有一款你安装过。注意&#xff0c;非按排名介绍。第一款:陌陌陌陌是一款基于地理位置的开放式移动视频社交应…

Android5.0新控件

谷歌在推出Android5.0的同时推出了一些新控件&#xff0c;Android5.0中最常用的新控件有下面5种。 1. CardView&#xff08;卡片视图&#xff09; CardView顾名思义是卡片视图&#xff0c;它继承FrameLayout。它是一个带圆角的背景和阴影FrameLayout。CardView被包装为一种布局…

Druid使用起步—在javaWeb项目中配置监控

原文章&#xff1a;http://my.oschina.net/u/568779/blog/152813 ---------------------------------------------------- druid wiki 当我们在javaWEB项目中使用到druid来作为我们的连接池的时候&#xff0c;一定不会忘了添加监控功能。下面我们就来看一下&#xff0c;在一个…

ip地址管理系统_门禁监控管理系统项目总结

门禁监控管理系统项目总结1、门禁管理设备(IFACE802) 16台 ,都有独立的网线到控制的房间。IP地址从 192.168.1.101--192.168.1.116。(地下一层 新家的最后一间是在附近的一个房间接的网线。一层的大门 有1台控制器和IFACE802 部署了2根网线)2、门禁控制的IP地址 192.168.1.1173…

平板电脑可以插u盘吗_有手机还需要平板电脑吗 酷比魔方iplay30平板电脑评测

原标题&#xff1a;有手机还需要平板电脑吗 酷比魔方iplay30平板电脑评测一、前言&#xff1a;前段时间我们一帮60岁左右的退休老头、老太太到农家乐去玩&#xff0c;玩累了就开始喝茶&#xff0c;一坐下来&#xff0c;人人都把手机掏出来&#xff0c;看着他们看手机的姿势&…

面向多媒体 linux 版本,基于MX Linux 的 AV Linux 新版发布,此AV非彼AV

原标题&#xff1a;基于MX Linux 的 AV Linux 新版发布&#xff0c;此AV非彼AV作者&#xff1a;Linux迷链接&#xff1a;https://www.linuxmi.com/av-linux-2020-11-23-mx-linux-19-3.html经过六个多月的开发&#xff0c;面向多媒体的AV Linux发行版已发行了新版本&#xff0c;…

Spring的PropertyPlaceholderConfigurer应用

1. PropertyPlaceholderConfigurer是个bean工厂后置处理器的实现&#xff0c;也就是 BeanFactoryPostProcessor接口的一个实现。PropertyPlaceholderConfigurer可以将上下文&#xff08;配置文 件&#xff09;中的属性值放在另一个单独的标准java Properties文件中去。在XML文件…

safari浏览器_吹爆苹果自带浏览器Safari,没有比它更贴心的浏览器了!!

大家一定觉得苹果的很多自带软件很难用对不对&#xff1f;嘻嘻&#xff0c;我觉得这正是苹果的魅力所在&#xff0c;它的很多产品与我们的正常使用习惯思维之间存在着一堵看不见的墙&#xff0c;只有当你你花点时间去研究、去驾驭它&#xff0c;把那堵墙给它推倒&#xff0c;然…

drcom linux怎么运行,linux下使用drcom登录认证

下载首先感谢开发者:https://github.com/drcoms/drcom-generic这个项目基于python实现了d,p,x版drcom认证,请到上述链接查看相关信息,项目的wiki简单介绍了各版本的使用方法.首先下载上面的drcom-generic,我将其下载到了家目录~/下,完整目录:~/drcom-generic取得必要信息下面启…

升级浏览器_星愿浏览器升级至6.3.2000.2001

玩懂手机网资讯&#xff0c;星愿浏览器升级至6.3.2000.2001&#xff0c;可能很多玩友都不熟悉这款浏览器&#xff0c;星愿浏览器(Twinkstar Browser)是一款使用chromium内核来自一首英文歌曲《 Twinkle, twinkle, little star》&#xff0c;选twinkle的名词twink&#xff0c;组…

mybatis3 添加ehcache支持

为了提高MyBatis的性能,有时候我们需要加入缓存支持,目前用的比较多的缓存莫过于ehcache缓存了,ehcache性能强大,而且位各种应用都提供了解决方案,在此我们主要是做查询缓存,提高查询的效率. 在Mybatis的官网上把集成ehcache的文档下载下来看了看,说的太简单了,对于新手很难理解…