Result 和 Option
Rust 的错误处理主要通过两个枚举类型:Result<T, E>
和 Option<T>
。这些类型让错误处理变得显式和安全。
Option 枚举
Option<T>
用于表示一个值可能存在也可能不存在的情况:
enum Option<T> {
Some(T),
None,
}
基本使用
fn find_word(text: &str, word: &str) -> Option<usize> {
text.find(word)
}
fn main() {
let text = "Hello, world!";
match find_word(text, "world") {
Some(index) => println!("找到 'world' 在位置: {}", index),
None => println!("没有找到 'world'"),
}
match find_word(text, "rust") {
Some(index) => println!("找到 'rust' 在位置: {}", index),
None => println!("没有找到 'rust'"),
}
}
Option 的常用方法
fn main() {
let some_number = Some(5);
let no_number: Option<i32> = None;
// is_some() 和 is_none()
println!("some_number 有值: {}", some_number.is_some());
println!("no_number 没有值: {}", no_number.is_none());
// unwrap() - 获取值,如果是 None 会 panic
println!("值: {}", some_number.unwrap());
// println!("{}", no_number.unwrap()); // 这会 panic!
// unwrap_or() - 提供默认值
println!("值或默认值: {}", no_number.unwrap_or(0));
// unwrap_or_else() - 使用闭包计算默认值
println!("值或计算的默认值: {}", no_number.unwrap_or_else(|| 42));
}
map 和 filter
fn main() {
let numbers = vec![Some(1), None, Some(3), Some(4), None];
// 使用 map 转换值
let doubled: Vec<Option<i32>> = numbers
.iter()
.map(|opt| opt.map(|x| x * 2))
.collect();
println!("翻倍后: {:?}", doubled);
// 过滤掉 None 值
let valid_numbers: Vec<i32> = numbers
.into_iter()
.filter_map(|opt| opt)
.collect();
println!("有效数字: {:?}", valid_numbers);
}
Result 枚举
Result<T, E>
用于表示操作可能成功或失败:
enum Result<T, E> {
Ok(T),
Err(E),
}
基本使用
fn divide(x: f64, y: f64) -> Result<f64, String> {
if y == 0.0 {
Err("不能除以零".to_string())
} else {
Ok(x / y)
}
}
fn main() {
match divide(10.0, 2.0) {
Ok(result) => println!("10 / 2 = {}", result),
Err(error) => println!("错误: {}", error),
}
match divide(10.0, 0.0) {
Ok(result) => println!("10 / 0 = {}", result),
Err(error) => println!("错误: {}", error),
}
}
Result 的常用方法
use std::fs::File;
use std::io::ErrorKind;
fn main() {
let file_result = File::open("hello.txt");
// is_ok() 和 is_err()
println!("文件打开成功: {}", file_result.is_ok());
// unwrap() - 获取成功值,失败时 panic
// let file = file_result.unwrap(); // 如果文件不存在会 panic
// unwrap_or_else() - 失败时执行闭包
let file = File::open("hello.txt").unwrap_or_else(|error| {
if error.kind() == ErrorKind::NotFound {
File::create("hello.txt").unwrap_or_else(|error| {
panic!("创建文件时出错: {:?}", error);
})
} else {
panic!("打开文件时出错: {:?}", error);
}
});
println!("文件处理完成");
}
expect 方法
expect
类似于 unwrap
,但允许我们选择 panic! 的错误信息:
use std::fs::File;
fn main() {
let file = File::open("hello.txt")
.expect("无法打开 hello.txt 文件");
println!("文件打开成功");
}
传播错误
当编写一个其实现会调用一些可能失败的操作的函数时,除了在这个函数中处理错误外,还可以选择让调用者知道这个错误并决定如何处理:
use std::fs::File;
use std::io::{self, Read};
fn read_username_from_file() -> Result<String, io::Error> {
let mut file = File::open("hello.txt")?;
let mut username = String::new();
file.read_to_string(&mut username)?;
Ok(username)
}
fn main() {
match read_username_from_file() {
Ok(username) => println!("用户名: {}", username),
Err(error) => println!("读取用户名失败: {}", error),
}
}
? 运算符
?
运算符是传播错误的简写:
use std::fs::File;
use std::io::{self, Read};
// 使用 ? 运算符的版本
fn read_username_from_file() -> Result<String, io::Error> {
let mut file = File::open("hello.txt")?;
let mut username = String::new();
file.read_to_string(&mut username)?;
Ok(username)
}
// 更简洁的版本
fn read_username_from_file_short() -> Result<String, io::Error> {
let mut username = String::new();
File::open("hello.txt")?.read_to_string(&mut username)?;
Ok(username)
}
// 最简洁的版本
fn read_username_from_file_shortest() -> Result<String, io::Error> {
std::fs::read_to_string("hello.txt")
}
fn main() {
match read_username_from_file() {
Ok(username) => println!("用户名: {}", username),
Err(error) => println!("错误: {}", error),
}
}
? 运算符与 Option
?
运算符也可以用于 Option
类型:
fn last_char_of_first_line(text: &str) -> Option<char> {
text.lines().next()?.chars().last()
}
fn main() {
let text = "Hello\nWorld";
match last_char_of_first_line(text) {
Some(ch) => println!("第一行的最后一个字符: {}", ch),
None => println!("没有找到字符"),
}
let empty_text = "";
match last_char_of_first_line(empty_text) {
Some(ch) => println!("第一行的最后一个字符: {}", ch),
None => println!("没有找到字符"),
}
}
组合 Option 和 Result
fn parse_and_double(s: &str) -> Result<Option<i32>, std::num::ParseIntError> {
match s.parse::<i32>() {
Ok(n) => Ok(Some(n * 2)),
Err(e) => Err(e),
}
}
// 使用 ? 运算符的版本
fn parse_and_double_short(s: &str) -> Result<Option<i32>, std::num::ParseIntError> {
Ok(Some(s.parse::<i32>()? * 2))
}
fn main() {
match parse_and_double("42") {
Ok(Some(n)) => println!("结果: {}", n),
Ok(None) => println!("没有值"),
Err(e) => println!("解析错误: {}", e),
}
match parse_and_double("abc") {
Ok(Some(n)) => println!("结果: {}", n),
Ok(None) => println!("没有值"),
Err(e) => println!("解析错误: {}", e),
}
}
自定义错误类型
use std::fmt;
#[derive(Debug)]
enum MathError {
DivisionByZero,
NegativeSquareRoot,
}
impl fmt::Display for MathError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
MathError::DivisionByZero => write!(f, "不能除以零"),
MathError::NegativeSquareRoot => write!(f, "不能计算负数的平方根"),
}
}
}
impl std::error::Error for MathError {}
fn divide(x: f64, y: f64) -> Result<f64, MathError> {
if y == 0.0 {
Err(MathError::DivisionByZero)
} else {
Ok(x / y)
}
}
fn sqrt(x: f64) -> Result<f64, MathError> {
if x < 0.0 {
Err(MathError::NegativeSquareRoot)
} else {
Ok(x.sqrt())
}
}
fn main() {
match divide(10.0, 2.0) {
Ok(result) => println!("10 / 2 = {}", result),
Err(error) => println!("错误: {}", error),
}
match sqrt(-4.0) {
Ok(result) => println!("√(-4) = {}", result),
Err(error) => println!("错误: {}", error),
}
}
实际应用示例
配置文件解析
use std::fs;
use std::collections::HashMap;
#[derive(Debug)]
enum ConfigError {
FileNotFound,
ParseError(String),
MissingKey(String),
}
impl std::fmt::Display for ConfigError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
ConfigError::FileNotFound => write!(f, "配置文件未找到"),
ConfigError::ParseError(msg) => write!(f, "解析错误: {}", msg),
ConfigError::MissingKey(key) => write!(f, "缺少配置项: {}", key),
}
}
}
impl std::error::Error for ConfigError {}
struct Config {
settings: HashMap<String, String>,
}
impl Config {
fn load(filename: &str) -> Result<Config, ConfigError> {
let content = fs::read_to_string(filename)
.map_err(|_| ConfigError::FileNotFound)?;
let mut settings = HashMap::new();
for line in content.lines() {
if line.trim().is_empty() || line.starts_with('#') {
continue;
}
let parts: Vec<&str> = line.split('=').collect();
if parts.len() != 2 {
return Err(ConfigError::ParseError(
format!("无效的行格式: {}", line)
));
}
settings.insert(
parts[0].trim().to_string(),
parts[1].trim().to_string(),
);
}
Ok(Config { settings })
}
fn get(&self, key: &str) -> Result<&String, ConfigError> {
self.settings.get(key)
.ok_or_else(|| ConfigError::MissingKey(key.to_string()))
}
fn get_or_default(&self, key: &str, default: &str) -> String {
self.settings.get(key)
.map(|s| s.clone())
.unwrap_or_else(|| default.to_string())
}
}
fn main() {
match Config::load("config.txt") {
Ok(config) => {
match config.get("database_url") {
Ok(url) => println!("数据库 URL: {}", url),
Err(error) => println!("错误: {}", error),
}
let port = config.get_or_default("port", "8080");
println!("端口: {}", port);
}
Err(error) => println!("加载配置失败: {}", error),
}
}
链式操作
fn process_data(input: &str) -> Result<i32, Box<dyn std::error::Error>> {
let trimmed = input.trim();
let parsed = trimmed.parse::<i32>()?;
let doubled = parsed * 2;
if doubled > 100 {
Err("结果太大".into())
} else {
Ok(doubled)
}
}
fn main() {
let inputs = vec!["42", " 25 ", "abc", "60"];
for input in inputs {
match process_data(input) {
Ok(result) => println!("'{}' -> {}", input, result),
Err(error) => println!("'{}' -> 错误: {}", input, error),
}
}
}
最佳实践
- 优先使用 Result 和 Option:而不是 panic! 或返回特殊值
- 使用 ? 运算符:简化错误传播
- 提供有意义的错误信息:使用 expect 而不是 unwrap
- 创建自定义错误类型:为复杂应用提供更好的错误处理
- 合理使用 unwrap:只在确定不会失败的情况下使用
// 好的错误处理示例
fn safe_divide(x: f64, y: f64) -> Option<f64> {
if y == 0.0 {
None
} else {
Some(x / y)
}
}
fn parse_positive_number(s: &str) -> Result<u32, String> {
let num = s.parse::<u32>()
.map_err(|_| format!("'{}' 不是有效的数字", s))?;
if num == 0 {
Err("数字必须大于零".to_string())
} else {
Ok(num)
}
}
fn main() {
// 使用 Option
match safe_divide(10.0, 2.0) {
Some(result) => println!("结果: {}", result),
None => println!("不能除以零"),
}
// 使用 Result
match parse_positive_number("42") {
Ok(num) => println!("解析成功: {}", num),
Err(error) => println!("错误: {}", error),
}
}