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…

Myeclipse/eclipse 安装查看class文件的插件

从http://www.oschina.net/project/tag/317/decompiler?lang19&os0&sortview 中下载了 也可到我的网盘&#xff1a;http://pan.baidu.com/s/1eS5BMcM 下载 Java反编译工具jad &#xff08;jad.exe&#xff09;和 Java反编译插件 Jadclipse &#xff08;net.sf.jadcl…

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

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

Https的前世今生

1、年前会议 马上要过年了&#xff0c;公司业务上的需求也少了很多&#xff0c;这不&#xff0c;王小二他们召开了一场技术会议&#xff0c;盘点年前能干点啥。 只见C哥写了一份清单&#xff0c;其中一项是全站升级https。 C哥说&#xff1a;https是一种趋势&#xff0c;但目前…

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实际存…

为什么使用NativeJdbcExtractor

原文&#xff1a;http://blog.csdn.net/hehexiaoyou/article/details/21019171---------------------------------------------------------------------------有时候必要会对数据库clob、 blob数据型进行操作&#xff0c;再加上spring 环境不得不要启用NativeJdbcExtractor 来…

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;非按排名介绍。第一款:陌陌陌陌是一款基于地理位置的开放式移动视频社交应…

通过Sqoop实现Mysql / Oracle 与HDFS / Hbase互导数据

下文将重点说明通过Sqoop实现Mysql与HDFS互导数据&#xff0c;Mysql与Hbase,Oracle与Hbase的互导最后给出命令。一、Mysql与HDFS互导数据环境&#xff1a; 宿主机器操作系统为Win7&#xff0c;Mysql安装在宿主机上&#xff0c;宿主机地址为192.168.66.963台虚拟机操作系统为Ubu…

Android5.0新控件

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

python中要使用导入全部的是什么符号-在python格式字符串中使用标点符号

这是因为您可以使用格式迷你语言来访问对象的属性.例如,我经常在自己的自定义类工作中使用它.假设我为每台需要处理的计算机定义了一个类. class Computer(object): def __init__(self,IP): self.IP IP 而现在我想对整个计算机做一些事情 list_comps [Computer(name,"19…

oracle 读懂10046视频,10046、10053、实操记录

10046是一个Oracle的内部事件(event)&#xff0c;通过设置这个事件可以得到Oracle内部执行系统解析、调用、等待、绑定变量等详细的trace信息&#xff0c;即帮助我们解析一条/多条SQL、PL/SQL语句的运行状态&#xff0c;这些状态包括&#xff1a;Parse/Fetch/Execute三个阶段中…

linux重定向文件被修改后,Linux服务器修改.htaccess文件实现301重定向

出于 SEO、PR 值传递、网址转换的目的&#xff0c;在网站初建和网站迁移时我们都需要使用 301 重定向&#xff0c;通常包括域名对域名&#xff0c;目录对目录和一个独立网址对另一个独立网址的重定向。在虚拟主机上作 301 重定向&#xff0c;最常用的方法有2种&#xff1a;第一…

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

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

[译] 想帮助用户做决定?你的APP可以这样设计!

原文地址&#xff1a;Design your app for decision-making原文作者&#xff1a;Jeni译文出自&#xff1a;掘金翻译计划本文永久链接&#xff1a;github.com/xitu/gold-m…译者&#xff1a;PTHFLY校对者&#xff1a;ryouaki想帮助用户做决定&#xff1f;你的APP可以这样设计&am…