c# 溢出抛异常_Rust竟然没有异常处理?

学习Rust最好的方法,就是和其他主流语言,比如Java、Python进行对比学习。不然怎么能get到它的特别呢?

dc7094eefbb85cbc770f76c6f53db525.png

1. 主流模式:try-catch-finally

基本上,当你学会了某种语言的try/catch,对这套机制的理解就能够迁移到其他语言上了。除了C++没有finally关键字外,像C#、Python、Java都有基本一致的异常处理逻辑:

  • 用try块包住可能会出现的异常;
  • 用catch将之捕获;
  • finally块统一处理资源的清理;
// Java
​
try{
​
}catch(FileNotFoundException f){
​
}catch(IOException i){
​
}finally{
​
}

对于自定义的函数,我们可以throw异常。

// Javaimport java.io.*;
public class ClassName
{public void deposit(double amount) throws RemoteException{// Method implementationthrow new RemoteException();}//Remainder of class definition
}

在这种异常处理系统中,对异常的定义是比较宽泛的:意料之外,情理之中。正是“异常”在语义上的模糊性,才产生了很多最佳实践来指导异常的使用。从“正常到异常的程度”上,大致上可以归为4类:

0 正常:不要用异常来进行流程控制,异常只用来处理“意外”

这条教导告诉我们,如果分不清“异常”,那么至少在“正常”的、没有意外的流程里,绝对不要用“异常机制来代替”。否则,代码可读性、可维护性将是灾难。

1 人造语义异常:如果主流程中存在一个连续的“闯关”pipeline(一组按顺序的调用,成功执行才能执行下一个,否则都算失败),那么可以使用try块来集中放置主流程代码,catch块来集中处理失败情况,避免if-else箭头形代码。

try {getSomeThing_1();getSomeThing_2();getSomeThing_3();
catch(Exception e) {// deal with it
}

这个技巧,和0 正常容易产生冲突,因为似乎有流程控制的嫌疑。但是凡事都有例外。这里的“意外”可以理解成一种语义上的“软意外”——即不能出错,区别于非法字符、找不到文件、连接不上等”硬意外“

2 情理中的意外,可恢复。

前面提到的非法字符、找不到文件、连接不上,基本是公认的“意外”情况,基本都使用抛出异常的方式,但是这种情况,通常都会进行捕获,并进行恢复。

3 无法意料的致命意外,不可恢复。

通常这种情况是,程序自身已经没有修复的空间,程序会中止:

  • Bug:逻辑错误导致的溢出、除0;
  • 致命错误:比如Java的JVM产生的Error;

2. Rust的Panic!

Rust里没有异常。

但如果非要和异常机制进行映射,Rust可以说做的相当决绝、非黑即白。

0 正常,以返回值的形式。

相当于压缩了上一节中的0、1、2项。没有什么情理中的意外,网络连不上、文件找不到、非法输入,统统都用返回值的方式。

1 致命错误,不可恢复,非崩不可。

一旦存在不可恢复的错误,Rust使用Panic!宏来终止程序(线程)。一旦Panic!宏出手,基本没得救(panic::catch_unwind是个例外,稍后说)。执行时默认会进行stack unwind(栈反解),一层层上去,直到线程的顶端。

有些情况Panic!是你的程序所依赖的库产生的,比如数组越界访问时的实现。

另一种情况,是你自己的程序逻辑判断产生了不可恢复的错误,可以手动触发Panic!宏来终止程序。Panic!的使用与throw很类似。

我写了一个小例子:打开一个文本文件,在写入之前,把它删掉,不仅没有收到Panic!,返回值错误也没有,居然写成功了。看来,这在Rust都不算事儿。着实让我惊讶了一小会儿。

use std::io::prelude::*;
use std::thread;
use std::time;
use std::fs::OpenOptions;
​
fn main() -> std::io::Result<()> {let mut f = OpenOptions::new().write(true).open("hello.txt")?;print!("{:?} n", f);
​// on the moment, manually remove the file hello.txtlet ten_millis = time::Duration::from_millis(10000);thread::sleep(ten_millis);
​print!("{:?} n", f);let r = f.write_all(b"Hello, world!")?;print!("Result is {:?} n", r);
​drop(f);
​Ok(())
}

输出如下:

看File结构,同一个句柄handle,但是path前后却发生了变化,文件都进回收站了,照样写你!

4e1b68d9e0c5af2af70771886863133a.png

3. Rust的返回值Result

前面提到了,对于可恢复的错误,Rust一律使用返回值来进行检查,而且提倡采用内置枚举Result,还在实践层面给了一定的约束:对于返回值为Result类型的函数,调用方如果没有进行接收,编译期会产生警告。很多库函数都通过Result来告知调用方执行结果,让调用方来决定是否严重到了使用Panic!的程度。

Result枚举的泛型定义如下:

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

在Rust标准库中,可以找到许多以Result命名的类型,它们通常是Result泛型的特定版本,比如File::open的返回值就是把T替换成了std::fs::File,把E替换成了std::io::Error。

枚举可以携带某个类型的数据,是Rust非常与众不同的特性

在上面的例子中,可能会有个疑问:并没有看到对Result的检查?

仔细看下,机关就在于最后的那个"?"

let mut f = OpenOptions::new().write(true).open("hello.txt")?;

或许是Rust对于“需要大量的返回值检查”的介意,于是有了“?”快捷运算符。

它可以避免模板代码。上面1行顶下面4行:

let f = OpenOptions::new().write(true).open("hello.txt");
let mut f = match f{Ok(file) => file,Err(e) => return Err(e),
};

4. panic::catch_unwind

最后,再来说个例外,panic::catch_unwind。

先看下它的用法:

use std::panic;
​
let result = panic::catch_unwind(|| {println!("hello!");
});
assert!(result.is_ok());
​
let result = panic::catch_unwind(|| {panic!("oh no!");
});
assert!(result.is_err());

没错,它的行为几乎就是try/catch了:panic!宏被捕获了,程序并也没有挂,返回了Err。尽管如此,Rust的目的并不是让它成为try/catch机制的实现,而是当Rust和其他编程语言互动时,避免其他语言代码块throw出异常。所以呢,错误处理的正道还是用Result

从catch_unwind的名字上,需要留意下unwind这个限定词,它意味着只有默认进行栈反解的panic可以被捕获到,如果是设为直接终止程序的panic,就逮不住了。

细节可进一步参考Rust Documentation。

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

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

相关文章

运营商 sni 服务器,什么是服务器名称指示(SNI)

在HTTPS 大热的今日&#xff0c;在过去的HTTP时代&#xff0c;解决基于名称的主机在同一IP地址上托管多个网站的问题并不难。当一个客户端请求某特定网站时&#xff0c;把请求的域名作为主机头(Host)放在HTTP Header中&#xff0c;服务器端知道应该把请求引向哪个域名&#xff…

水面反光如何拍摄_拍摄水面反射的创意

很多人都喜欢拍摄倒影&#xff0c;不同介质表面的倒影可以提供给我们各种各样不同的创意拍摄思路。但是从技术角度上来说&#xff0c;拍摄倒影其实一点都不简单&#xff0c;相比那些常规的拍摄内容&#xff0c;倒影显然需要一些技巧&#xff0c;像是对焦、取景、拍摄手法以及后…

登和平视显示无法连接服务器,提醒信息的推送方法和装置、平视显示器HUD及服务器...

1.一种提醒信息的推送方法&#xff0c;应用于车辆上的显示装置&#xff0c;其特征在于&#xff0c;所述方法包括&#xff1a;接收预设服务器发送的与所述车辆相匹配的目标订单的订单信息&#xff0c;其中所述订单信息用于指示所述车辆的订单状态&#xff1b;确定与所述订单状态…

db2 语句包括不必要的列表_DB2 SQL0956C 数据库堆中没有足够的处理空间可用来处理此语句...

问题描述&#xff1a;执行db2 create db 命令时,报错&#xff1a;DB2 SQL0956C 数据库堆中没有足够的处理空间可用来处理此语句。数据库配置信息如下&#xff1a;数据库管理器配置节点类型 带有本地客户机和远程客户机的企业服务器…

ios 搜集崩溃信息上传服务器,iOS 收集APP崩溃

UncaughtExceptionHandler收集APP崩溃信息&#xff0c;上传到服务器&#xff0c;用于分析统计.一些特殊场景&#xff0c;集成了某个第三方库&#xff0c;但不想它收集我们APP的崩溃信息.收集APP崩溃信息//苹果提供异常捕获相关函数/** 获取异常捕获句柄A pointer to the top-le…

python编程第八讲答案_小甲鱼Python第八讲课后习题

i 0string ‘ILoveFishC.com‘while i print(i)i 1答&#xff1a;i 0string ‘ILoveFishC.com‘length len(string)while i< length:print(i)i 1动手&#xff1a;0.设计一个验证用户密码程序&#xff0c;用户只有三次机会输入错误&#xff0c;不过如果用户输入的内容中包…

电脑摄像头未能创建连接服务器,Win7中摄像头提示未能创建视频预览错误怎么办...

最近有不少用户在我们网站上提问说他们在打开摄像头的时候&#xff0c;就会遇到打开摄像头的时候提示“未能创建视频预览&#xff0c;请检查设备连接”的问题&#xff0c;这是怎么回事呢&#xff0c;出现这样的原因的话有可能是电脑中毒了或者某些相关的服务没有启动导致的&…

华硕服务器主板型号命名规则,常见主板命名规则

最近因为想要组装 NAS&#xff0c;所以简单的了解了一下主板的命名规则。这里将总结一下。多数厂家遵循一般的规律&#xff1a;处理器类型芯片组芯片类型基本后缀芯片组名字由芯片厂商决定&#xff0c;AMD 在发布锐龙后抢了英特尔的命名方式&#xff0c;从低到高端 A320, B350,…

Ubuntu 常用命令之 date 命令用法介绍

&#x1f4d1;Linux/Ubuntu 常用命令归类整理 date命令在Ubuntu系统中用于显示或设置系统的日期和时间。 date常见的参数 -d, --dateSTRING&#xff1a;显示STRING指定的时间&#xff0c;而不是当前时间。-u, --utc, --universal&#xff1a;显示或设置协调世界时间。-R, --…

局部配置和全局配置_06. 教你零基础搭建小程序(解读全局配置文件-tabBar字段)...

自从开始在知乎上发教程后&#xff0c;发现一个事情啊&#xff0c;大家对于我写的教程&#xff0c;都在偷偷地收藏~~但是&#xff0c;却没有银点赞~知乎这个平台吧&#xff0c;点赞的分量比收藏更大&#xff0c;这里也不是要赞&#xff08;我知道自己还能做的更好&#xff09;&…

python编程类型_Python 基础编程 数据类型(一)

Python 对象类型对象类型类型的类名描述Characterchr单字节字符&#xff0c;在字符串中使用Integerint32位整数Floatfloat双精度(64位)浮点数Long integerlong任意大的整数Complexcomplex复数Character stringstr有序(数组)字符集Dictionarydict键 / 值 对字典Tupletuple不可变…

mac系统在云服务器地址,mac如何登陆云服务器地址

mac如何登陆云服务器地址 内容精选换一换本章节指导用户获取云服务器的IP地址等网卡信息。进入弹性云服务器页面。进入云服务器详情页面。进入网卡详情页面。网卡详情页面进入虚拟IP详情页&#xff0c;即可根据云服务器网卡信息找到绑定的虚拟IP地址。本章节介绍如何使用弹性云…

时间转化_Excel常见时间日期函数全讲解,10个函数教你如何进行日期转化

在工作中我们经常会碰到一些需要转化或者计算时间日期的工作&#xff0c;这里就需要我们用到一些常见的Excel时间日期函数。今天我们就通过十个案例来教大家&#xff0c;如何在实际工作中对时间日期进行转化处理。函数一、显示当前日期函数TODAY()2018/10/10函数二、显示当前日…

线扫相机 编码器_面阵和线扫工业相机选型

版权声明&#xff1a;本文为博主原创文章&#xff0c;遵循 CC 4.0 BY-SA 版权协议&#xff0c;转载请附上原文出处链接和本声明。本文链接&#xff1a;https://blog.csdn.net/snowbird13/article/details/64919984一. 面阵相机和镜头选型已知&#xff1a;被检测物体大小为A*B,要…

hdc mfc 画扇形图_MFC画图总结-DIB图形绘制

http://blog.sina.com.cn/s/blog_4c75bd8c0100zsw7.html绘制DIB图到到设备&#xff0c;明白一点。DIB设备无关图形向设备CDC上绘的话&#xff0c;肯定是须要经过图形pixel像素转换的&#xff0c;要转换成设备的颜色像素格式。參考这段说明:DIB的颜色信息储存在自己的颜色表中。…

alive的不生效 keep vue_webpack打包vue项目 keep-alive不生效

项目会使用tab功能&#xff0c;进而需要对页面数据进行缓存。在本地开发的时候&#xff0c;能够正常缓存&#xff0c;但是在使用webpack打包后&#xff0c;上传至服务器发现缓存失效&#xff0c;导致有些业务出错。路由代码&#xff1a;module.exports (file: string) > { …

回旋滚动_中频炉电动旋转轴承,管道回旋轴承,电炉旋转轴承

中频炉电动旋转轴承&#xff0c;管道回旋轴承&#xff0c;电炉旋转轴承&#xff0c;&#xff0c;管道旋转轴承自带双边法兰,在集尘罩管道做法兰和主管道的法兰连接,旋转轴承可以360度旋转&#xff0c;除尘器管道轴承可以同时承受较大的轴向、径向负荷和倾覆力矩。除尘器旋转轴承…

dao层如何调用对象_你的项目应该如何正确分层?

你好&#xff0c;欢迎收听极客视点。 说起应用分层&#xff0c;大部分人都会认为这不是很简单嘛&#xff0c;就Controller、Service、Mapper三层。但在“简单”背后&#xff0c;很多人并没有将各层级的职责划分清楚。比如在很多代码中&#xff0c;Controller比Service还多&…

combobox 如何让text居中_MFC 中ListBox 与 ComboBox 中的文本如何实现水平居中与垂直居中 - 小众知识...

MFC 中&#xff0c; ListBox 与 ComboBox 中的项在设置了高度的情况下如何实现文本的水平居中与垂直居中&#xff1f;&#xff1f;&#xff1f;ListBox 与 ComboBox 中的数据均为动态添加文本内容含有数字、英文、中文void CMyComboBox::DrawItem(LPDRAWITEMSTRUCT lpDrawItemS…

android 子module混淆_Android 多模块打包混淆填坑记

最近有个 sdk 的项目使用了多模块(Module)开发&#xff0c;然后提供 jar 包给接入者使用&#xff0c;要求大部分类是混淆过的&#xff0c;保留几个接口&#xff0c;Android Studio 能够导出 aar 文件&#xff0c;对于导出 jar 却要大费一番周折。我在网上找到这个比较靠谱的解决…