我注意到Rust没有例外。如何在Rust中进行错误处理以及常见的陷阱是什么?有没有办法通过加注,捕获,重新加注和其他东西来控制流量?我发现这方面的信息不一致。
答案 0 :(得分:25)
Rust通常以两种方式解决错误:
无法恢复的错误。一旦你panic!
,那就是它。您的程序或线程因为遇到无法解决的问题而中止,并且其不变量已被违反。例如。如果您在UTF-8字符串中找到无效序列。
可恢复的错误。在某些文档中也称为失败。您可以发出Option<T>
或Result<T, E>
,而不是恐慌。在这些情况下,您可以分别选择有效值Some(T)
/ Ok(T)
或无效值None
/ Error(E)
。通常None
用作null
替换,表示缺少该值。
现在来了困难的部分。应用
有时处理Option
是一个痛苦的问题,你几乎可以保证得到一个值,而不是一个错误。
在这些情况下,使用unwrap
完全没问题。 unwrap
将Some(e)
和Ok(e)
变为e
,否则会引起恐慌。展开是一种将可恢复错误转变为不可恢复的工具。
if x.is_some() {
y = x.unwrap(); // perfectly safe, you just checked x is Some
}
在if
内部阻止它完全可以解开,因为它应该永远不会恐慌,因为我们已经检查过它是Some
x.is_some()
。< / p>
如果您正在编写库,则不鼓励使用unwrap
,因为当它发生恐慌时,用户无法处理错误。此外,未来的更新可能会更改不变量。想象一下,如果上面的例子有if x.is_some() || always_return_true()
。不变量会发生变化,unwrap
可能会出现恐慌。
?
运算符/ try!
宏 ?
运算符或try!
宏是什么?一个简短的解释是,它要么返回Ok()
内的值,要么过早地返回错误。
以下是运算符或宏扩展到的简化定义:
macro_rules! try {
($e:expr) => (match $e {
Ok(val) => val,
Err(err) => return Err(err),
});
}
如果你这样使用它:
let x = File::create("my_file.txt")?;
let x = try!(File::create("my_file.txt"));
它会将其转换为:
let x = match File::create("my_file.txt") {
Ok(val) => val,
Err(err) => return Err(err),
};
缺点是你的函数现在返回Result
。
Option
和Result
有一些方便的方法,允许以可理解的方式链接和处理错误。类似and
,and_then
,or
,or_else
,ok_or
,map_err
等方法。
例如,如果您的值有问题,您可以使用默认值。
let x: Option<i32> = None;
let guaranteed_value = x.or(Some(3)); //it's Some(3)
或者,如果您想将Option
变为Result
。
let x = Some("foo");
assert_eq!(x.ok_or("No value found"), Ok("foo"));
let x: Option<&str> = None;
assert_eq!(x.ok_or("No value found"), Err("No value found"));
这只是您可以做的事情的简要介绍。有关更多说明,请查看:
答案 1 :(得分:1)
如果你需要终止一些独立的执行单元(一个网络请求、一个视频帧处理、一个 GUI 事件、一个要编译的源文件)但不是你的所有应用程序的完整性,有一个函数 std::panic::catch_unwind调用闭包,如果发生,则捕获解除恐慌的原因。
let result = panic::catch_unwind(|| {
panic!("oh no!");
});
assert!(result.is_err());
我不会授予此闭包对任何可能超过它的变量或任何其他全局状态的写访问权限。
文档还说该函数也可能无法捕获某些类型的恐慌。