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;像是对焦、取景、拍摄手法以及后…

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

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

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;&…

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

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

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

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

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

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

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

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

mysql索引_MySQL索引介绍和实战

索引是什么MySQL官方对索引的定义为&#xff1a;索引(Index)是帮助MySQL高效获取数据的数据结构。可以得到索引的本质&#xff1a;索引是数据结构&#xff0c;索引的目的是提高查询效率&#xff0c;可以类比英语新华字典&#xff0c;根据目录定位词语如果没有目录呢&#xff0c…

mysql安装需要注意什么意思_mysql 安装过程及注意事项

1.1. 下载&#xff1a;我下载的是64位系统的zip包&#xff1a;下载zip的包&#xff1a;下载后解压&#xff1a;D:\软件安装包\mysql-5.7.20-winx641.2. 配置环境变量&#xff1a;变量名&#xff1a;MYSQL_HOME变量值&#xff1a;E:\mysql-5.7.20-winx64path里添加&#xff1a;%…

gitlab 端口_安装Gitlab-注意端口

文档本身并没有什么特殊&#xff0c;安装也很简单&#xff0c;只是修改端口这里如果有需要的可以看一下安装Gitlab[rootdeploy ~]# sudo yum -y install gitlab-ce默认端口是8080&#xff0c;避免冲突还是修改一下[rootlocalhost ~]# cat /etc/gitlab/gitlab.rb |grep 192.168.…

MySQL read-c_技术分享 | MySQL C API 参数 MYSQL_OPT_READ_TIMEOUT 的一些行为分析

作者&#xff1a;戴岳兵MYSQL_OPT_READ_TIMEOUT 是 MySQL c api 客户端中用来设置读取超时时间的参数。在 MySQL 的官方文档中&#xff0c;该参数的描述是这样的&#xff1a;MYSQL_OPT_READ_TIMEOUT (argument type: unsigned int *)The timeout in seconds for each attempt t…

mysql解释中fitered_MySQL的explain中的参数说明

1、id每个被独立执行的操作的标识&#xff0c;表示对象被操作的顺序&#xff1b;id值大&#xff0c;先被执行&#xff1b;如果相同&#xff0c;执行顺序从上到下。若没有子查询和联合查询&#xff0c;id则都是1。Mysql会按照id从大到小的顺序执行query&#xff0c;在id相同的情…

vue脚手架搭建项目_复习之vue脚手架搭建项目的两种方法

安装脚手架node 版本要求&#xff1a; > 8.9 。关于旧版本&#xff1a;如果在这之前已经全局安装了旧版本的vue-cli(1.x 或 2.x)&#xff0c;那么需要先卸载掉。卸载旧版本运行&#xff1a;npm uninstall vue-cli -g 或 yarn global remove vue-cli。安装vue/cli&#xff1a…

pythonsocket中tcp通信接收不到数据_TCP 为什么三次握手而不是两次握手(正解版)...

先说结论为了实现可靠数据传输&#xff0c; TCP 协议的通信双方&#xff0c; 都必须维护一个序列号&#xff0c; 以标识发送出去的数据包中&#xff0c; 哪些是已经被对方收到的。 三次握手的过程即是通信双方相互告知序列号起始值&#xff0c; 并确认对方已经收到了序列号起始…

mysql无法启动如何备份文件_mysql 5.7 停电导致无法启动、如何备份数据,重新安装mysql...

用于记录服务器停电导致&#xff0c;mysql启动失败后&#xff0c;如何备份数据&#xff0c;重新安装mysql&#xff0c;主要分为数据备份&#xff0c;mysql重新安装。1、mysql无法启动时&#xff0c;进行数据备份。执行&#xff1a;systemctl start mysqld&#xff0c;启动失败。…

python tkinter entry默认值_Python ---(六)Tkinter窗口组件:Entry

The Tkinter Entry Widget##简介Entry(输入框)组件通常用于获取用户的输入文本。##何时使用 Entry 组件&#xff1f;Entry 组件仅允许用于输入一行文本&#xff0c;如果用于输入的字符串长度比该组件可显示空间更长&#xff0c;那内容将被滚动。这意味着该字符串将不能被全部看…

java 静态块初始化_简单了解java中静态初始化块的执行顺序

这篇文章主要介绍了简单了解java中静态初始化块的执行顺序,文中通过示例代码介绍的非常详细&#xff0c;对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下在java中&#xff0c;其应该是先于所有的方法执行。下面是测试代码&#xff1a;public class Test1 {st…

java 常量区存放 new_java---堆、栈、常量池的存储数据

说到Java中堆、栈和常量池&#xff0c;首先还是看看他们各自存放的数据类型吧&#xff01;栈&#xff1a;Java的JVM的内存可分为3个区&#xff1a;堆(heap)、栈(stack)和方法区(method)也叫静态存储区。堆区:(存放所有new出来的对象&#xff1b;)1.存储的全部是对象&#xff0c…