Panic
类似于 JS 中的 throw new Error
,在 Rust 中是调用 panic!
这个宏抛出异常
fn main() {
panic!("error");
}
如果加上 RUST_BACKTRACE
的环境变量执行 Rust 程序,当抛出异常时会把详细的堆栈信息打印出来
$ RUST_BACKTRACE=1 cargo run
Result
类似于 Option<T>
,针对错误信息,Rust 提供了一个叫 Result
的枚举。
enum Result<T, E> {
Ok(T),
Err(E),
}
比如文件操作,返回的就是 Result 这个枚举,可以通过 match 获取到正确的结果。
use std::fs::File;
fn main() {
let f = File::open("src/text.txt");
let mut f = match f {
Ok(file) => file,
Err(e) => panic!("read file filed, {:?}", e),
};
let mut str = String::new();
let r = f.read_to_string(&mut str);
match r {
Ok(r) => {
println!("content is {}, size is {}", str, r);
},
Err(e) => panic!("read file filed, {:?}", e),
}
}
unwrap & expect
按照枚举的用法,如果总是要 match 的话太繁琐,所以 Rust 给 Result
枚举提供了一些好用的方法,可以通过 unwrap
或者 expect
来简写逻辑。
use std::fs::File;
fn main() {
// 其实 fs 提供了更简单的方法,可以直接 fs::read_to_string("src/text.txt")。
// 这里 demo 先不这么写了。
let mut f = File::open("src/text.txt").unwrap();
let mut str = String::new();
let r = f.read_to_string(&mut str).expect("read file filed");
println!("content is {}, size is {}", str, r);
}
unwrap
或者 expect
都相当于帮做了 match 的逻辑,如果成功返回结果,失败则抛出异常,两个函数的不同之处就是 unwrap
不接收自定义的错误信息,但是 expect
会接收。
自己写的函数也可以返回 Result 这个枚举。跟 Option<T>
一样,Result 的枚举可以直接使用( Ok 和 Err )
use rand::{thread_rng, Rng};
fn main() {
let f = test_result().unwrap();
println!("{}", f);
}
fn test_result() -> Result<String, String> {
let rand: u8 = thread_rng().gen_range(0..10);
if rand > 5 {
Ok("123")
} else {
Err("123")
}
}
除了使用 unwrap
,还可以用 unwrap_or_else
来对错误进行更进一步的处理,unwrap_or_else
支持传入一个回调函数,也就是出错时候的处理,比如下面的代码中,当出错时判断错误类型是文件不存在,那就创建一个文件并且写入内容。
let file = "src/text.txt";
let mut f = File::open(&file).unwrap_or_else(|err| {
if err.kind() == ErrorKind::NotFound {
println!("file is not found, create it");
// create 创建的 fd 是 write only 的,不能 read
let mut wf = File::create(&file).unwrap();
wf.write_all(b"hello world");
wf
} else {
panic!("other error");
}
});
|err| {}
这种写法是类似于 JS 中的回调函数,相当于(err) => {}
,举个例子( 泛型跟 TS 里的差不多 )
fn main() {
let r2 = test_fn_once(|str| {
let u: u32 = str.parse().expect("failed to parse to number");
u
});
println!("{}", r2);
}
// FnOnce 的意思是这个函数只能调一次
fn test_fn_once<T, F: Fn(String) -> T> (f: F) -> T{
f(String::from("123"))
}
更简易的写法
比 unwrap
或 expect
还有更简单的写法就是用 ?
操作符。两者的差异是,前者是遇到错误会 panic!
,后者则是返回 Result 不继续执行而不是 panic!
,比如以下代码。
use std::error::Error;
use std::fs::File;
fn main() -> Result<(), Box<dyn Error>> {
let f = File::open("hello.txt")?;
Ok(())
}
有点像 JS 中,try catch 后 return error 的感觉。正因为用了 ?
就会有返回值,所以就算是 main 函数,也要声明返回类型,Box<dyn Error>
代表任意错误。
通过 ? 的方式就可以链式调用了。
use std::error::Error;
use std::fs::File;
use std::io::Read;
fn main() -> Result<(), Box<dyn Error>> {
let mut s = String::new();
File::open("hello.txt")?.read_to_string(&mut s)?;
Ok(())
}