MST

星途 面试题库

面试题:Rust中自定义错误类型及与panic机制的配合

在Rust中,如何自定义一个错误类型并实现std::error::Error trait?当发生自定义错误时,如何选择是通过Result进行常规错误处理,还是触发panic机制?请结合一个实际应用场景说明你的决策过程。
36.1万 热度难度
编程语言Rust

知识考点

AI 面试

面试题答案

一键面试

自定义错误类型并实现 std::error::Error trait

  1. 定义错误类型结构体:首先定义一个结构体来表示自定义错误。
struct MyError {
    message: String,
}
  1. 实现 std::fmt::Display trait:为了能将错误以字符串形式展示,需要实现 Display trait。
use std::fmt;
impl fmt::Display for MyError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.message)
    }
}
  1. 实现 std::error::Error traitError trait 要求实现 source 方法,该方法用于获取错误的根源。如果没有具体的根源,可以返回 None
impl std::error::Error for MyError {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        None
    }
}

通过 Result 进行常规错误处理与触发 panic 机制的选择

  1. Result 常规错误处理:适用于错误是预期的并且程序可以从中恢复的情况。例如,在文件读取操作中,文件不存在是一种常见错误,程序可以选择提示用户或尝试其他路径。
fn read_file() -> Result<String, MyError> {
    let path = "nonexistent_file.txt";
    std::fs::read_to_string(path).map_err(|e| MyError {
        message: format!("Failed to read file: {}", e),
    })
}
  1. 触发 panic 机制:适用于错误是不可恢复的,例如程序内部逻辑错误、违反了不应该被违反的不变量等情况。比如在一个实现简单除法的函数中,除数为零是一种不应该发生的情况,因为在业务逻辑中不允许除数为零。
fn divide(a: i32, b: i32) -> i32 {
    if b == 0 {
        panic!("Division by zero is not allowed");
    }
    a / b
}

实际应用场景及决策过程

假设开发一个用户登录系统。在验证用户输入的密码时,如果密码哈希验证失败,这是一个预期的错误,因为用户可能输入错误密码。此时适合使用 Result 进行常规错误处理,返回一个错误信息提示用户重新输入。

fn verify_password(input: &str, stored_hash: &str) -> Result<(), MyError> {
    // 实际密码验证逻辑,这里用简单模拟
    if input != "correct_password" {
        Err(MyError {
            message: "Password verification failed".to_string(),
        })
    } else {
        Ok(())
    }
}

另一方面,如果在初始化密码哈希算法时出现错误,比如算法库文件损坏,这是一个不可恢复的错误,因为没有正确的哈希算法就无法安全验证密码。这种情况下应该触发 panic,因为继续运行系统可能导致安全问题。

fn init_hash_algorithm() {
    // 假设这里初始化哈希算法失败
    panic!("Failed to initialize hash algorithm");
}

总结来说,对于外部输入相关且程序可以恢复的错误,使用 Result 处理;对于程序内部逻辑错误或不可恢复的情况,使用 panic