面试题答案
一键面试自定义错误类型并实现 std::error::Error
trait
- 定义错误类型结构体:首先定义一个结构体来表示自定义错误。
struct MyError {
message: String,
}
- 实现
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)
}
}
- 实现
std::error::Error
trait:Error
trait 要求实现source
方法,该方法用于获取错误的根源。如果没有具体的根源,可以返回None
。
impl std::error::Error for MyError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
None
}
}
通过 Result
进行常规错误处理与触发 panic
机制的选择
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),
})
}
- 触发
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
。