对比编程语言的四种错误处理方法,哪种才是最优方案?

△点击上方“Python猫”关注 ,回复“1”领取电子书

643779f667858bfb6c081e10c39504cf.jpeg

作者:Andrea Bergia

译者:豌豆花下猫@Python猫

英文:Error handling patterns

转载请保留作者及译者信息!

错误处理是编程的一个基本要素。除非你写的是“hello world”,否则就必须处理代码中的错误。在本文中,我将讨论各种编程语言在处理错误时使用的最常见的四种方法,并分析它们的优缺点。

关注不同设计方案的语法、代码可读性、演变过程、运行效率,将有助于我们写出更为优雅和健壮的代码。

返回错误代码

这是最古老的策略之一——如果一个函数可能会出错,它可以简单地返回一个错误代码——通常是负数或者null。例如,C 语言中经常使用:

FILE* fp = fopen("file.txt" , "w");
if (!fp) {// 发生了错误
}

这种方法非常简单,既易于实现,也易于理解。它的执行效率也非常高,因为它只需要进行标准的函数调用,并返回一个值,不需要有运行时支持或分配内存。但是,它也有一些缺点:

  • 用户很容易忘记处理函数的错误。例如,在 C 中,printf 可能会出错,但我几乎没有见过程序检查它的返回值!

  • 如果代码必须处理多个不同的错误(打开文件,写入文件,从另一个文件读取等),那么传递错误到调用堆栈会很麻烦。

  • 除非你的编程语言支持多个返回值,否则如果必须返回一个有效值或一个错误,就很麻烦。这导致 C 和 C++ 中的许多函数必须通过指针来传递存储了“成功”返回值的地址空间,再由函数填充,类似于:

my_struct *success_result;
int error_code = my_function(&success_result);
if (!error_code) {// can use success_result
}

众所周知,Go 选择了这种方法来处理错误,而且,由于它允许一个函数返回多个值,因此这种模式变得更加人性化,并且非常常见:

user, err = FindUser(username)
if err != nil {return err
}

Go 采用的方式简单而有效,会将错误传递到调用方。但是,我觉得它会造成很多重复,而且影响到了实际的业务逻辑。不过,我写的 Go 还不够多,不知道这种印象以后会不会改观!😅

异常

异常可能是最常用的错误处理模式。try/catch/finally 方法相当有效,而且使用简单。异常在上世纪 90 年代到 2000 年间非常流行,被许多语言所采用(例如 Java、C# 和 Python)。

与错误处理相比,异常具有以下优点:

  • 它们自然地区分了“快乐路径”和错误处理路径

  • 它们会自动从调用堆栈中冒泡出来

  • 你不会忘记处理错误!

然而,它们也有一些缺点:需要一些特定的运行时支持,通常会带来相当大的性能开销。

此外,更重要的是,它们具有“深远”的影响——某些代码可能会抛出异常,但被调用堆栈中非常远的异常处理程序捕获,这会影响代码的可读性。

此外,仅凭查看函数的签名,无法确定它是否会抛出异常。

C++ 试图通过throws 关键字来解决这个问题,但它很少被使用,因此在 C++ 17 中已被弃用 ,并在 C++ 20 中被删除。此后,它一直试图引入noexcept 关键字,但我较少写现代 C++,不知道它的流行程度。

(译者注:throws 关键字很少使用,因为使用过于繁琐,需要在函数签名中指定抛出的异常类型,并且这种方法不能处理运行时发生的异常,有因为“未知异常”而导致程序退出的风险)

Java 曾试图使用“受检的异常(checked exceptions)”,即你必须将异常声明为函数签名的一部分——但是这种方法被认为是失败的,因此像 Spring 这种现代框架只使用“运行时异常”,而有些 JVM 语言(如 Kotlin)则完全抛弃了这个概念。这造成的结果是,你根本无法确定一个函数是否会抛出什么异常,最终只得到了一片混乱。

(译者注:Spring 不使用“受检的异常”,因为这需要在函数签名及调用函数中显式处理,会使得代码过于冗长而且造成不必要的耦合。使用“运行时异常”,代码间的依赖性降低了,也便于重构,但也造成了“异常源头”的混乱)

回调函数

另一种方法是在 JavaScript 领域非常常见的方法——使用回调,回调函数会在一个函数成功或失败时调用。这通常会与异步编程结合使用,其中 I/O 操作在后台进行,不会阻塞执行流。

例如,Node.JS 的 I/O 函数通常加上一个回调函数,后者使用两个参数(error,result),例如:

const fs = require('fs');
fs.readFile('some_file.txt', (err, result) => {if (err) {console.error(err);return;}console.log(result);
});

但是,这种方法经常会导致所谓的“回调地狱”问题,因为一个回调可能需要调用其它的异步 I/O,这可能又需要更多的回调,最终导致混乱且难以跟踪的代码。

现代的 JavaScript 版本试图通过引入promise 来提升代码的可读性:

fetch("https://example.com/profile", {method: "POST", // or 'PUT'
}).then(response => response.json()).then(data => data['some_key']).catch(error => console.error("Error:", error));

promise 模式并不是最终方案,JavaScript 最后采用了由 C#推广开的 async/await 模式,它使异步 I/O 看起来非常像带有经典异常的同步代码:

async function fetchData() {try {const response = await fetch("my-url");if (!response.ok) {throw new Error("Network response was not OK");}return response.json()['some_property'];} catch (error) {console.error("There has been a problem with your fetch operation:", error);}
}

使用回调进行错误处理是一种值得了解的重要模式,不仅仅在 JavaScript 中如此,人们在 C 语言中也使用了很多年。但是,它现在已经不太常见了,你很可能会用的是某种形式的async/await。

函数式语言的 Result

我最后想要讨论的一种模式起源于函数式语言,比如 Haskell,但是由于 Rust 的流行,它已经变得非常主流了。

它的创意是提供一个Result类型,例如:

enum Result<S, E> {Ok(S),Err(E)
}

这是一个具有两种结果的类型,一种表示成功,另一种表示失败。返回结果的函数要么返回一个Ok 对象(可能包含有一些数据),要么返回一个Err 对象(包含一些错误详情)。函数的调用者通常会使用模式匹配来处理这两种情况。

为了在调用堆栈中抛出错误,通常会编写如下的代码:

let result = match my_fallible_function() {Err(e) => return Err(e),Ok(some_data) => some_data,
};

由于这种模式非常常见,Rust 专门引入了一个操作符(即问号 ?) 来简化上面的代码:

let result = my_fallible_function()?;   // 注意有个"?"号

这种方法的优点是它使错误处理既明显又类型安全,因为编译器会确保处理每个可能的结果。

在支持这种模式的编程语言中,Result 通常是一个 monad,它允许将可能失败的函数组合起来,而无需使用 try/catch 块或嵌套的 if 语句。

(译者注:函数式编程认为函数的输入和输出应该是纯粹的,不应该有任何副作用或状态变化。monad 是一个函数式编程的概念,它通过隔离副作用和状态来提高代码的可读性和可维护性,并允许组合多个操作来构建更复杂的操作)

根据你使用的编程语言和项目,你可能主要或仅仅使用其中一种错误处理的模式。

不过,我最喜欢的还是 Result 模式。当然,不仅是函数式语言采用了它,例如,在我的雇主 lastminute.com 中,我们在 Kotlin 中使用了 Arrow 库,它包含一个受 Haskell 强烈影响的类型Either。我有计划写一篇关于它的文章,最后感谢你阅读这篇文章,敬请保持关注😊。

译注:还有一篇《Musings about error handling mechanisms in programming languages》文章,同样分析了不同编程语言在错误处理时的方案。它还介绍了 Zig 编程语言的做法、Go 语言的 defer 关键字等内容,可以丰富大家对这个话题的理解,推荐一读。

相关链接:

[1] Error handling patterns: https://andreabergia.com/blog/2023/05/error-handling-patterns
[2] 译者: https://pythoncat.top
[3] 弃用: https://en.cppreference.com/w/cpp/language/exceptspec
[4] noexcept: https://en.cppreference.com/w/cpp/language/noexceptspec
[5] “受检的异常”: https://www.baeldung.com/java-checked-unchecked-exceptions
[6] 抛弃了这个概念: https://kotlinlang.org/docs/exceptions.html#the-nothing-type
[7] 回调: https://kotlinlang.org/docs/exceptions.html#the-nothing-type
[8] “回调地狱”: http://callbackhell.com/
[9] promise: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/GlobalObjects/Promise
[10] Haskell: https://hackage.haskell.org/package/base-4.18.0.0/docs/Data-Either.html
[11] Rust: https://andreabergia.com/blog/2022/11/languages-opinion-part-two-rust
[12] monad: https://en.wikipedia.org/wiki/Monad(functionalprogramming)
[13] lastminute.com: https://lastminute.com
[14] Arrow: https://arrow-kt.io
[15] Musings about error handling mechanisms in programming languages: https://www.amazingcto.com/best-way-to-handle-errors-for-a-programming-language

13f55f0990e31a5b086c9fc12556a0e2.gif

Python猫技术交流群开放啦!群里既有国内一二线大厂在职员工,也有国内外高校在读学生,既有十多年码龄的编程老鸟,也有中小学刚刚入门的新人,学习氛围良好!想入群的同学,请在公号内回复『交流群』,获取猫哥的微信(谢绝广告党,非诚勿扰!)~

还不过瘾?试试它们

不想当作家的程序员写不出 Redis

ChatGPT 开源了第一款插件,都来学习一下源码吧!

为什么 Python、Ruby 等语言弃用了自增运算符?

为什么 Python 不用声明类型?

80 个例子,彻底掌握Python日期时间处理!

Python 为什么使用缩进来划分代码块?

如果你觉得本文有帮助

请慷慨分享点赞,感谢啦

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

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

相关文章

巴比特 | 元宇宙每日必读:不再盲目迷信硬件掘金,大厂XR集体后撤,业内人士建议多条腿走路,生态和内容才有更广阔市场...

摘要&#xff1a;据极点商业报道&#xff0c;元宇宙凉热之间&#xff0c;大厂集体收缩XR业务。是舆论定调的风口已过&#xff0c;还是如业内人士所言&#xff0c;进入理智探索时代&#xff1f;为何短短半年时间内&#xff0c;大厂们态度就发生了截然不同的变化&#xff1f;“与…

学会这些思维模型,窥见查理·芒格的致胜秘笈(二)

目录 导语 模型五&#xff1a;SCQA模型 模型六&#xff1a;问题树模型 模型七&#xff1a;MECE原则 模型八&#xff1a;SWOT分析 模型九&#xff1a;波特五力模型 模型十&#xff1a;波斯顿矩阵 导语 读者朋友们好&#xff0c;在我上一篇文章介绍了几个最核心和底层的模…

产品读书《穷查理宝典:查理.芒格智慧箴言录》

要通过提升自己&#xff0c;来强化对世界的感知&#xff0c;内在的能力才能增长&#xff0c;和世界的大趋势才能互相匹配。这种匹配度提高了&#xff0c;无论你是用来投资、创业&#xff0c;还是做其他事情&#xff0c;都会无往而不利。 查理芒格在书中反复强调的4点内容有&a…

芒格最新演讲:中国的水有些聪明人已经蹚进去了,时候到了更多人会进场

图文来源&#xff1a;雪球 译者&#xff1a;RanRan 我们在说某个人有常识的时候&#xff0c;我们其实是说&#xff0c;他具备平常人没有的常识。人们都以为具备常识很简单&#xff0c;其实很难。 2月14日&#xff0c;95岁的芒格参加了Daily Journal 公司年会&#xff0c;发表了…

站在大模型新风口的云知声:十一年创业之路引领智能语音技术变革

AGI的目标是实现人类般的通用智能&#xff0c;这意味着AI可以像人类一样理解任意通用任务&#xff0c;并以人类的智力水平执行完成。基本上&#xff0c;除了自我意识的生成&#xff0c;AGI就是人类对人工智能的终极梦想了。在AGI曙光照进现实之际&#xff0c;云知声化身为那位站…

在MT4上使用KDJ指标

KDJ指标就是随机指标&#xff0c;由K线、D线和J线这三条曲线共同构成&#xff0c;通过分析图表&#xff0c;我们可以得出&#xff0c;K、D、J分别用不同的颜色线条来表示&#xff0c;所谓的K线是指快速确认线&#xff0c;D线就是指慢速主干线&#xff0c;而J线则为方向明暗线。…

同花顺资金监测精鹰指标公式源码 通过资金监测强弱

使用方法&#xff1a; 指标不含未来,是通过资金监测强弱,资金是不会骗人 当红柱有粉色帽子或红柱上方金色线均表示资金流入 源代码&#xff1a; VAR1:VOL/((HIGH-LOW)*2-ABS(CLOSE-OPEN)); 主动买盘:IF(CLOSE>OPEN,VAR1*(HIGH-LOW),IF(CLOSE<OPEN,VAR1*((HIGH-OPEN)(…

django中使用auth.authenticate在用户名和密码都正确的情况下返回值依然为None的可能原因

毕业设计做了一个系统&#xff0c;其中涉及到用户修改密码&#xff0c;然后发现密码改完之后即使输入了也登不上去&#xff0c;最终定位到问题出在auth.authenticate上&#xff0c;它返回的是None&#xff0c;在csdn上找了好久&#xff0c;全都是说在创建的时候使用django自带的…

美国公司裁员潮可视化;GitHub + Kaggle + InfoQ:3份报告回顾中国开发者的2022 | ShowMeAI每周通讯 #005-01.07

这是ShowMeAI每周通讯的第5期。通讯聚焦AI领域本周热点&#xff0c;及其在各圈层泛起的涟漪&#xff1b;关注AI技术进步&#xff0c;并提供我们的商业洞察。欢迎关注与订阅&#xff01; 导读&#xff1a;2023年第1周&#xff0c;美国科技大厂的裁员信息&#xff0c;终于还是来了…

如何转换图片格式?教你三招一键轻松转换图片格式

之前有朋友跟我吐槽说&#xff0c;有时候保存到电脑上的图片原来是正常的&#xff0c;结果保存下来以后就打不开了&#xff0c;搞的他非常苦恼。其实这个问题也很好解决&#xff0c;一般这种问题都是由于图片格式导致的&#xff0c;只需要将图片格式转换成jpg、png等常见格式就…

手把手教你免费、批量转换HEIC图片到JPG

iPhone手机更新IOS11系统后&#xff0c;相机拍摄的文件格式为HEIC格式&#xff0c;很多软件都无法打开。找了半天也是各种收费&#xff0c;就比如某款软件居然收费&#xff0c;还有年费88块&#xff01;你们怎么好意思收费的&#xff1f; 要么就是在线的转换&#xff0c;一张张…

PS把变成人物照片插画效果

算不算插画不是很懂&#xff0c;又有点类似&#xff0c;效果就是下图的这样&#xff0c;使用陌鱼社区动作&#xff1a;简单时尚手绘人物插画效果PS动作&#xff0c;下面是一些效果图&#xff0c;喜欢的可以试一下 01、双击图案&#xff08;.pat&#xff09;&#xff0c;Adobe P…

通过python我实现了照片转化为动漫模式,媳妇儿再也不用愁没有好看的头像了~

​ 最近某音上的动漫特效特别火&#xff0c;很多人都玩着动漫肖像&#xff0c;我媳妇儿也不例外。看着她这么喜欢这个特效&#xff0c;我决定做一个图片处理工具&#xff0c;这样媳妇儿的动漫头像就有着落了。 编码 为了快速实现我们的目标&#xff0c;我们就不自己写图片处理程…

将照片转换成漫画风格的API推荐

这段时间休息的时候发现了一个很有意思的API——将照片转换成漫画风格API&#xff0c;是在 APISpace 这个接口服务平台发现的。它里面的很多接口都非常的好用&#xff0c;使用起来也很方便&#xff0c;所有的接口都可以提供免费的试用&#xff0c;有需要的同学可以去看看~ 效果…

APISpace 将照片转化成漫画风格API

APISpace 的 将照片转化成漫画风格API&#xff0c;会自动为你将照片转换成漫画风格&#xff0c;无须使用Photoshop 等图片编辑软件、无须任何技术基础。输入图片的url&#xff0c;即可得到漫画风格的图片。 APISpace 上面还多各种各样的API&#xff0c;包括常见的短信、物流、…

Photo2Cartoon,照片图片批量转漫画

当你宅在家里冲浪&#xff0c;当你和驴友出行&#xff0c;当你打开自己的相册。。。。有没有想过让自己的照片换个样子。照片转漫画是一个不错的idea&#xff0c;换个角度欣赏会获得更多的意想不到&#xff0c;不是吗&#xff1f;试试看&#xff01; 首先&#xff0c;进入“图…

ACC编程应用挑战赛决赛真题

目录 哈喽 真题 题目一——圈地盘 题目二——门票 题目三——免单挑战 题目四——逃脱 题目五——一夫当关 题目六——游乐场 最后 哈喽 Hello!昨天是六一儿童节&#xff0c;可在雅安接连发生了6.1、4.3级地震。我在成都&#xff0c;虽然我没感觉到&#xff0c;但我们…

北师大计算机学院保研,北师大信息科学与技术学院保研-北京师范大学信息科学与技术学院保研推荐免试研究生方法...

109梁竞月北京大学电子信息科学类(电子、微电子、计算机软件与理论86.00硕士 110林武桃武汉大学信息安全计算机软件与理论90.00硕士 111刘沛东北京大学电子信息科学类计算机软件与理论88.00硕士 112骆宇冲北京大学电子信息科学类计算机软件与理论83.00硕士 113蒙力北京大学电子…

【DFS专题训练】踏青 C++程序题 连通块问题

题目描述 小白和他的朋友周末相约去召唤师峡谷踏青。他们发现召唤师峡谷的地图是由一块一块格子组成的&#xff0c;有的格子上是草丛&#xff0c;有的是空地。草丛通过上下左右 4 个方向扩展其他草丛形成一片草地&#xff0c;任何一片草地中的格子都是草丛&#xff0c;并且所有…

JavaScript内置对象

JavaScript内置对象 1.什么是对象&#xff1f; JavaScript中的所有事物都是对象&#xff0c;如字符串、数值、数组、函数等&#xff0c;每个对象带有属性和方法。 对象的属性&#xff1a;反映该对象某些特定的性质的&#xff0c;如&#xff1a;字符串的长度、图像的长度等。…