» Rust快速入门 » 2. 高级篇 » 2.6 错误处理

错误处理

panic

panic 打印错误消息,展开堆栈,并退出程序。

fn main() {
    let divisor = 0;
    if divisor == 0 {
        // 如果除数为零,则使用自定义错误消息引发 panic
        panic!("Attempted to divide by zero!");
    }
    let result = 42 / divisor;
    println!("Result: {}", result);
}

Option

std 库中,使用 Option<T> 枚举来表示可能不存在的情况。它有两种选项:

  • Some(T): 类型为 T 的元素
  • None: 没有元素

这两个选项可以通过 match 显式处理,也可以通过 unwrap 隐式处理。隐式处理将返回内部元素或引发 panic

fn main() {
    let some_value: Option<&str> = Some("literank");
    let unwrapped_value = some_value.unwrap();
    println!("Unwrapped value: {}", unwrapped_value);

    // 使用 None 变体创建一个 Option
    let none_value: Option<i32> = None;
    // 尝试展开 None 变体(将引发 panic)
    let unwrapped_none = none_value.unwrap();
    println!("This line will not be reached due to the panic above");
}

使用 ? 解包

你可以通过使用 match 语句来解包 Option,但使用 ? 运算符通常更方便。

fn process_option_value(value: Option<i32>) -> Option<i32> {
    // 使用 `?` 运算符展开 Option,如果是 None 则提前返回 None
    let unwrapped_value = value?;
    let result = unwrapped_value * 2;
    Some(result)
}

Result

ResultOption 类型的高配版本,其用于描述可能的错误而不是可能的值缺失。

Result<T, E> 可能有两种结果:

  • Ok(T):成功后类型为 T 的元素
  • Err(E):失败后包含类型为 E 元素的错误
#[derive(Debug)]
struct CustomError;

fn divide(a: i32, b: i32) -> Result<i32, CustomError> {
    if b == 0 {
        Err(CustomError)
    } else {
        Ok(a / b)
    }
}

main 中使用 Result

Result 类型也可以是 main 函数的显式返回类型。

fn main() -> Result<(), CustomError> {
    // 如果失败,? 运算符将错误在主函数中返回
    let result = divide(58, 2)?;
    println!("Result is: {}", result);
    Ok(())
}

? 几乎等同于 unwrap,但在遇到 Err 时,它 return 而不是 panic

迭代 Result

map 操作是有可能失败的。

fn main() {
    let strings = vec!["literank", "58", "42"];
    let numbers: Vec<_> = strings
        .into_iter()
        .map(|s| s.parse::<i32>())
        .collect();
    println!("Results: {:?}", numbers);
    // Results: [Err(ParseIntError { kind: InvalidDigit }), Ok(58), Ok(42)]
}

可以使用 filter_map 忽略失败的项。

fn main() {
    let strings = vec!["literank", "58", "42"];
    let numbers: Vec<_> = strings
        .into_iter()
        .filter_map(|s| s.parse::<i32>().ok())
        .collect();
    println!("Results: {:?}", numbers);
    // Results: [58, 42]
}

还可以使用 map_err 将失败的项收集起来。

fn main() {
    let strings = vec!["42", "literank", "58", "300", "18"];
    let mut errors = vec![];
    let numbers: Vec<_> = strings
        .into_iter()
        .map(|s| s.parse::<u8>())
        .filter_map(|r| r.map_err(|e| errors.push(e)).ok())
        .collect();
    println!("Numbers: {:?}", numbers);
    // Numbers: [42, 58, 18]
    println!("Errors: {:?}", errors);
    // Errors: [ParseIntError { kind: InvalidDigit }, ParseIntError { kind: PosOverflow }]
}

或者,可以使用 partition 以更简单的方式执行此操作。

fn main() {
    let strings = vec!["42", "literank", "58", "300", "18"];
    let (numbers, errors): (Vec<_>, Vec<_>) = strings
        .into_iter()
        .map(|s| s.parse::<i32>())
        .partition(Result::is_ok);
    println!("Numbers: {:?}", numbers);
    // Numbers: [Ok(42), Ok(58), Ok(300), Ok(18)]
    println!("Errors: {:?}", errors);
    // Errors: [Err(ParseIntError { kind: InvalidDigit })]
}

Result 实现了 FromIterator,以便将结果的向量 (Vec<Result<T, E>>) 转换为带有向量的结果 (Result<Vec<T>, E>)。一旦遇到 Result::Err,迭代终止。

fn main() {
    let strings = vec!["literank", "58", "42"];
    let numbers: Result<Vec<_>, _> = strings
        .into_iter()
        .map(|s| s.parse::<i32>())
        .collect();
    println!("Results: {:?}", numbers);
    // Results: Err(ParseIntError { kind: InvalidDigit })
}

代码挑战

尝试修改编辑器中提供的代码,以处理 divide_numbers 中的错误。

Loading...
> 此处输出代码运行结果
上页
下页