38、可处理错误和Result枚举
Fallibility(可错性)
重新回顾之前练习中的Ticket::new
函数
impl Ticket {
pub fn new(
title: String,
description: String,
status: Status
) -> Ticket {
if title.is_empty() {
panic!("Title cannot be empty");
}
if title.len() > 50 {
panic!("Title cannot be longer than 50 bytes");
}
if description.is_empty() {
panic!("Description cannot be empty");
}
if description.len() > 500 {
panic!("Description cannot be longer than 500 bytes");
}
Ticket {
title,
description,
status,
}
}
}
一旦其中一项检查失败,函数就会panic
。这种情况并不理想,因为它不给调用者处理错误的机会。
Result
赶紧端上来吧,这是Rust处理错误的主要机制。
Result
类型
Result
是在标准库中定义的枚举类型。
enum Result<T, E> {
Ok(T),
Err(E),
}
它有两种变体:
Ok(T)
表示操作成功,它保存操作的输出T
。Err(E)
表示操作失败,它保存E
,即发生的错误。
Ok
和Err
都是通用类型,允许我们分别为成功和错误的情况指定我们自己的类型。
无异常机制
Rust中的可恢复错误用数值表示。
它们只是一种类型的实例,像任何其他值一样被传递和操作。这与其他语言(比如Python或C#)有显著的差别。在其他语言中,异常用于发出错误信号。
异常会创建一个难以推理的单独控制流路径。光看函数的签名,我们不知道它是否能抛出异常。光看函数的签名,我们也不知道它能抛出那些异常类型。
这必须阅读函数的文档或查看其实现才能知道。
异常处理逻辑的局部性很差:抛出异常的代码与捕获异常的代码相差甚远,两者之间没有直接联系。
Fallibility(可错性)被编码在类型系统中
使用Result
,Rust会强迫我们再函数签名中对可错性进行编码。
如果函数可能失败(而且我们希望调用者有处理异常的机会),那么它必须返回一个结果。
// Just by looking at the signature, you know that this function
// can fail. You can also inspect `ParseIntError` to see what
// kind of failures to expect.
fn parse_int(s: &str) -> Result<i32, ParseIntError> {
// ...
}
这就是Result
的最大优势:它让可错性变得明确。
但是要记住,panic是存在的。他们不会被类型系统所追踪,就像其它语言中的异常一样。但它们是针对不可恢复的错误的,应该尽量少用。
练习
这两节的练习,确实让人身心愉悦,我们直接看错误处理的题目:
// TODO: Convert the `Ticket::new` method to return a `Result` instead of panicking.
// Use `String` as the error type.
#[derive(Debug, PartialEq)]
pub struct Ticket {
title: String,
description: String,
status: Status,
}
#[derive(Debug, PartialEq)]
pub enum Status {
ToDo,
InProgress { assigned_to: String },
Done,
}
impl Ticket {
pub fn new(title: String, description: String, status: Status) -> /* TODO */ {
if title.is_empty() {
return Err("Title cannot be empty".into());
}
if title.len() > 50 {
return Err("Title cannot be longer than 50 bytes".into());
}
if description.is_empty() {
return Err("Description cannot be empty".into());
}
if description.len() > 500 {
return Err("Description cannot be longer than 500 bytes".into());
}
/* TODO */
}
}
答案:
impl Ticket {
pub fn new(title: String, description: String, status: Status) -> Result<Ticket, String> {
if title.is_empty() {
return Err("Title cannot be empty".into());
}
if title.len() > 50 {
return Err("Title cannot be longer than 50 bytes".into());
}
if description.is_empty() {
return Err("Description cannot be empty".into());
}
if description.len() > 500 {
return Err("Description cannot be longer than 500 bytes".into());
}
Ok(Ticket{
title,
description,
status,
})
}
}
感觉还可以,没前几天那么高难度了,其实我之前也看过这方面的书,也就很好理解了。
这一节的学习就先到这里了。
阅读剩余
版权声明:
作者:CN059
链接:https://www.cn059.com/2025/09/16/38%e3%80%81%e5%8f%af%e5%a4%84%e7%90%86%e9%94%99%e8%af%af%e5%92%8cresult%e6%9e%9a%e4%b8%be.html
文章版权归作者所有,未经允许请勿转载。
THE END