Rust错误处理
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Rust错误处理相关的知识,希望对你有一定的参考价值。
参考技术A 简单的错误处理要不是使用“?”,Rust的错误处理会显得有些不够灵巧。要使用“?”我们需要声明返回值类型为Result类型,这种类型可以包含任何具备std::error::Error特征从而可以转换为Bax<Error>类型的错误类型。
拿我们需要处理IO错误和字符串转换为数字错误举例:
use std::fs::File;
use std::io::prelude::*;
use std::error::Error;
fn run(file: &str) -> Result<i32, Box<Error>>
let mut file = File::open(file)?;
let mut contents = String::new();
file.read_to_string(&mut contents)?;
Ok(contents.trim().parse()?)
这里使用了俩个"?"处理可能发生的IO错误:打开文件错误和读取内容为string错误。
使用了一个“?”处理可能发生的类型转换错误。
最后我们将结果包装为Ok类型。Rust可以中返回值中判断出parse的结果为i32类型。
简化Result类型的声明比较容易,我们可以定义一个自己的Result类型,比如:
type BoxResult<T> = Result<T, Box<Error>>
但是,我们程序还需要自定义Error类型,那我们就需要做些其他工作:
可以实现Debug特征
必须实现Display特征
必须实现Error特征
就像这样:
//error1.rs
use std::error::Error;
use std::fmt;
#[derive(Debug)]
struct MyError
details: String
impl MyError
fn new(msg: &str) -> MyError
MyErrordetails: msg.to_string()
impl fnt::Display for MyError
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Reuslt
write!(f, "", self.details)
impls Error for MyError
fn description(&self) -> &str
&self.details
//一个测试我们自定义错误的函数
fn raises_my_error(yes: bool) -> Result<(), MyError>
if yes
Err(MyError::new("borked"))
else
Ok(())
输入Result<T, MyError>比较麻烦,所以很多模块都定义了自己的Result类型,这样可以少敲几下键盘。比如IO模块定义了io::Reust<T>代替Result<T, io::Error>使用。
在下面的例子中演示的是怎么处理String类型转化为浮点数类型可能出现错误的情况。
现在我们知道使用"?"可以方便的把有错误的表达式转换为Err返回。这种转换是通过From特征实现的。
你可以继续使用这种方便的转换,这在一些比较简单的应用中是个不错的选择,接下来我们演示的场景会复杂一些。
ParseFloatError实现了Error特征,所以它具备description()方法。
use std::num::ParseFloatError;impl From for MyError fn from(err: ParseFloatError) -> Self MyError::new(err.description()) fn parse_f64(s: &str, yes: bool) -> Result raise_my_error(yes)?; let x: f64 = s.parse()? Ok(x)fn main() println!(" :?", parse_f64("42", false)); println!(" :?", parse_f64("42", true)); println!(" :?", parse_64("?42", false));
执行结果会是这样:
Ok(42)
Err(MyError details: “borked”)
Err(MyError details: “invalid float literal”)
未完待续
原文地址
Rust语言教程 - 错误处理和可选值
Rust语言教程(6) - 错误处理和可选值
Rust的错误处理
从前面的学习中,我们对于Rust的错误处理应该已经有个体感了。Rust的返回值中,同时包含了正确情况下的值和错误情况下的报错信息的值。
以上一讲的读取标准输入值为例,我们其实没有处理返回值:
let sin1 = std::io::stdin().read_line(&mut str8);
如果要处理的话,需要如何做呢?
使用match表达式处理错误
我们可以使用match表达式,来同时对正确和错误两种情况进行处理。
我们来看处理上面的返回值的例子:
println!("Please input a number:");
let mut str8 = String::with_capacity(255);
let sin1 = std::io::stdin().read_line(&mut str8);
match sin1
Ok(size1) =>
println!("The size read from stdin is:",size1);
let num8 = str8.trim().parse::<i32>().unwrap();
println!("",num8);
Err( err) =>
println!("Read error!",err);
通过判断状态处理错误
如果觉得match表达式嵌套得太多了看起来烦,没关系,我们也可以直接通过判断状态来进行处理。状态不看就unwrap有点太武断了,早晚panic,加个is_ok的判断吧:
let rnum = str8.trim().parse::<i32>();
if rnum.is_ok()
println!("",rnum.unwrap());
在这种情况下如何获取错误信息呢?我们可以通过err方法。但是,err方法返回的是一个Option可选值,与错误值类似,我们还需要加一重处理,我们来看例子:
let rnum = str8.trim().parse::<i32>();
if rnum.is_ok()
println!("",rnum.unwrap());
else
let err1 = rnum.err();
if err1.is_some()
println!("",rnum.err().unwrap());
错误时给个默认值
有的时候,有错误也不重要,我们给一个默认值就好。
比如上面的parse整数,如果失败就给个0值,提升容错性:
println!("",rnum.unwrap_or(0));
当然,如果只给个默认值,可能还不够,我们还想做一些进一步的操作。这时,我们可以给一个闭包,在闭包里实现更多的控制,比如可以打印个日志之类的:
rnum.unwrap_or_else(|err|
return 0;
);
panic时给出定制化的提示
像上面的输入的例子,如果用户给出的值不合法,将程序panic掉也是一种办法。
但是我们可以输出自己的提示,这样用户知道是什么问题,下一次输入的时候可以避免。
我们还是修改上面的例子:
println!("The size read from stdin is:",size1);
let rnum = str8.trim().parse::<i32>();
rnum.expect("Please input an integer");
输出的panic信息如下:
thread 'main' panicked at 'Please input an integer: ParseIntError kind: InvalidDigit ', src/main.rs:303:18
stack backtrace:
0: rust_begin_unwind
at /rustc/e1884a8e3c3e813aada8254edfa120e85bf5ffca/library/std/src/panicking.rs:495:5
1: core::panicking::panic_fmt
at /rustc/e1884a8e3c3e813aada8254edfa120e85bf5ffca/library/core/src/panicking.rs:92:14
2: core::option::expect_none_failed
at /rustc/e1884a8e3c3e813aada8254edfa120e85bf5ffca/library/core/src/option.rs:1268:5
3: core::result::Result<T,E>::expect
at /Users/lusinga/.rustup/toolchains/stable-x86_64-apple-darwin/lib/rustlib/src/rust/library/core/src/result.rs:933:23
4: tools::test
at ./src/main.rs:303:13
5: tools::main
at ./src/main.rs:36:5
6: core::ops::function::FnOnce::call_once
at /Users/lusinga/.rustup/toolchains/stable-x86_64-apple-darwin/lib/rustlib/src/rust/library/core/src/ops/function.rs:227:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
复合类型
随着学习的深入,靠简单类型可能已经难以满足我们的需求了。我们现在可以引入一些复合类型来方便编程。
元组
同其它语言一样,Rust的元组tuple也是用括号括起来的一组可以为不同类型的值的组合。
我们来个例子:
let t1 = (1.0, "Test");
要访问元素的话,可以使用数字做下标,比如第1个就是t1.0:
let t1 = (1.0, "Test");
println!(" ",t1.0,t1.1);
元组可以为空,()叫做unit。
结构体
元组只有个顺序,如果想给字段指定个名字的话,可以使用结构体。
比如我们定义实部和虚部构成的复数:
struct Complex
real : i32,
imagine : i32
初始化可以使用下面的方法:
let c1 = Complexreal :0, imagine: 1;
访问的话大家都很熟悉了:
println!("+i",c1.real,c1.imagine);
枚举enum
Rust的enum有点意思,用来表示可以是这种类型,也可以是另一种类型的类型。
比如我们可以定义一种既可能是数字又可能是字符串的类型:
enum NumberOrText
Int(i32),
Text(String)
其中的字段名可以用来初始化类型变量的值:
enum NumberOrText
Int(i32),
Text(String)
let e1 = NumberOrText::Int(1);
let e2 = NumberOrText::Text("1".to_string());
那么,这个有两种可能的类型如何处理呢?我们还是用match表达式来处理:
match e1
NumberOrText::Int(value) => println!("",value),
NumberOrText::Text(value) => println!("",value)
可选值
在Rust中,如果返回值可能为空,可以使用可选值来封装之。这样就可以避免很多其它语言可能出现的NullPointerException之类的错误。
前面介绍的enum虽然看起来有点奇怪,但是却是实现可选值的有力武器。
没错,Rust的Option类型就是用enum来实现的:
pub enum Option<T>
None,
Some(T),
我们来看个例子,可能是整数,也可能是空值的类型,可以用Option等表示:
let o1 : Option<i32> = Some(1_i32);
let o2 : Option<i32> = None;
如何使用Option呢?我们可以使用is_some来判断是不是有值,有的话就可以unwrap:
let o3 = Some(2_i32);
if o3.is_some()
println!("",o3.unwrap());
当然,不嫌烦的话,match表达式仍然是很好的选择:
let o1 : Option<i32> = Some(1_i32);
let v1 = match o1
Option::Some(value) => value,
Option::None => 0_i32
;
同样,我们也可以用unwrap_or来指定默认值:
let o2 : Option<i32> = None;
println!("o2=",o2.unwrap_or(0));
小结
有了enum的基础,我们再回望一下错误处理的Result类型,它其实也是个enum:
pub enum Result<T, E>
Ok(T),
Err(E),
以上是关于Rust错误处理的主要内容,如果未能解决你的问题,请参考以下文章
Rust Web 全栈开发之 Web Service 中的错误处理
如何使用适用于 DynamoDb 的 AWS Rust 开发工具包编写惯用的 Rust 错误处理?