本文介绍了Rust中的错误处理机制,通过温度检查的示例展示了不可恢复的 Panic 和可恢复的 Result 的使用,讲解了 ? 运算符简化错误传播的方法及其意义。
Rust 将错误分为两类:不可恢复错误(panic)和可恢复错误(Result)。想象你正在组装一架模型飞机:如果发现缺少关键零件,组装无法继续,只能停下来检查问题,这就是不可恢复的 panic;但如果只是螺丝拧不紧,你可以换个工具继续尝试,这就是可恢复的 Result。
不可恢复错误通常意味着程序遇到了无法继续运行的情况,比如访问超出数组范围或断言失败。在Rust中,我们可以用 panic! 宏显式触发这种错误。当 panic 发生时,程序会打印错误信息,清理栈上的资源(通过栈展开),然后退出。
假设我们在计算一个数的平方根时,要求输入必须是非负数:
fn square_root(n: f64) {
if n < 0.0 {
panic!("Cannot compute square root of a negative number: {}", n);
}
println!("Square root of {} is {}", n, n.sqrt());
}
fn main() {
square_root(4.0); // 输出: Square root of 4 is 2
square_root(-1.0); // panic! 程序终止,打印错误信息
}
运行这段代码时,传入负数,panic! 会中断程序。
对于可以预见和处理的错误,Rust 提供了 Result<T, E> 类型。它是一个枚举,定义如下:
enum Result<T, E> {
Ok(T), // 成功时返回的值
Err(E), // 失败时的错误信息
}
假设我们要检查一个温度是否在安全范围内:
fn check_temperature(temp: f64) -> Result<f64, String> {
if temp > 100.0 {
Err(format!("Temperature {}°C is too high!", temp))
} else if temp < -50.0 {
Err(format!("Temperature {}°C is too low!", temp))
} else {
Ok(temp)
}
}
fn main() {
let temp = check_temperature(25.0);
match temp {
Ok(value) => println!("Safe temperature: {}°C", value),
Err(msg) => println!("Error: {}", msg),
}
let too_hot = check_temperature(150.0);
match too_hot {
Ok(value) => println!("Safe temperature: {}°C", value),
Err(msg) => println!("Error: {}", msg),
}
}
输出:
Safe temperature: 25°C
Error: Temperature 150°C is too high!
这里,Result让我们既能处理成功的情况(Ok),也能捕获错误(Err),避免程序直接崩溃。
手动用 match 处理 Result 虽然可行,但代码很容易变得冗长。Rust 提供了 ? 运算符,像一个自动化的助手,把错误传播给调用者,同时简化成功情况的处理。
假设我们要组合两个检查步骤:温度和湿度:
fn check_temperature(temp: f64) -> Result<f64, String> {
if temp > 100.0 {
Err(format!("Temperature {}°C is too high!", temp))
} else if temp < -50.0 {
Err(format!("Temperature {}°C is too low!", temp))
} else {
Ok(temp)
}
}
fn check_humidity(humidity: f64) -> Result<f64, String> {
if humidity > 90.0 {
Err(format!("Humidity {}% is too high!", humidity))
} else if humidity < 10.0 {
Err(format!("Humidity {}% is too low!", humidity))
} else {
Ok(humidity)
}
}
fn check_conditions(temp: f64, humidity: f64) -> Result<String, String> {
let safe_temp = check_temperature(temp)?; // 如果出错,传播错误
let safe_humidity = check_humidity(humidity)?; // 如果出错,传播错误
Ok(format!("Conditions safe: {}°C, {}%", safe_temp, safe_humidity))
}
fn main() {
match check_conditions(25.0, 50.0) {
Ok(result) => println!("{}", result),
Err(e) => println!("Error: {}", e),
}
match check_conditions(150.0, 50.0) {
Ok(result) => println!("{}", result),
Err(e) => println!("Error: {}", e),
}
}
输出:
Conditions safe: 25°C, 50%
Error: Temperature 150°C is too high!
? 运算符在这里起到了关键作用:如果 check_temperature 或 check_humidity 返回 Err,错误会直接传播给 main,否则返回值被解包为 Ok 中的内容。这种方式让代码更简洁。
小结
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!