Ch 3: std::result - Result错误处理
Result<T, E> 是 Rust 中用于表示操作结果的标准枚举类型,特别适合处理可能失败的操作(如 I/O、解析、计算等)。与 Option 不同,Result 可以携带错误信息。本章将详细讲解 Result 的常用方法。
2. Result 基础回顾
Section titled “2. Result 基础回顾”pub enum Result<T, E> { Ok(T), Err(E),}Result 是 Rust 错误处理的核心,与 Option 一样都是类型安全的。
3. map_err - 转换错误类型
Section titled “3. map_err - 转换错误类型”map_err 在 Err 上应用函数转换错误类型,Ok 直接穿透:
方法签名:
pub fn map_err<F, O>(self, op: O) -> Result<T, F>where O: FnOnce(E) -> F,示例:
use std::io;use std::num::ParseIntError;
fn main() -> Result<(), ParseIntError> { let result: Result<i32, ParseIntError> = "42".parse();
// 将 ParseIntError 转换为 io::Error let result: Result<i32, io::Error> = result .map_err(|e| io::Error::new(io::ErrorKind::Other, e));
println!("{:?}", result); // Ok(42) Ok(())}实用场景 - 自定义错误类型:
use std::fmt;
#[derive(Debug)]enum AppError { NotFound(String), InvalidInput(String), IoError(std::io::Error),}
impl fmt::Display for AppError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { AppError::NotFound(s) => write!(f, "Not found: {}", s), AppError::InvalidInput(s) => write!(f, "Invalid input: {}", s), AppError::IoError(e) => write!(f, "IO error: {}", e), } }}
fn read_number() -> Result<i32, std::io::Error> { Ok(42)}
fn main() -> Result<(), AppError> { let num = read_number() .map_err(AppError::IoError)?;
println!("Number: {}", num); Ok(())}4. and_then - 链式Result操作
Section titled “4. and_then - 链式Result操作”and_then 在 Ok 上执行返回 Result 的函数,用于链式操作:
方法签名:
pub fn and_then<U, F>(self, op: F) -> Result<U, E>where F: FnOnce(T) -> Result<U, E>,示例:
fn main() { let result: Result<i32, &str> = Ok(5);
// 链式操作 let value = result .and_then(|n| { if n > 0 { Ok(n * 2) } else { Err("Value must be positive") } }) .and_then(|n| Ok(n + 10));
println!("{:?}", value); // Ok(20)
// 错误会短路 let bad_result: Result<i32, &str> = Ok(-1); let value = bad_result .and_then(|n| { if n > 0 { Ok(n * 2) } else { Err("Value must be positive") } });
println!("{:?}", value); // Err("Value must be positive")}实用场景 - 数据库查询:
#[derive(Debug)]struct User { id: u32, name: String,}
fn find_user(id: u32) -> Result<User, &'static str> { if id == 1 { Ok(User { id: 1, name: "Alice".to_string() }) } else { Err("User not found") }}
fn get_user_email(user: User) -> Result<String, &'static str> { if user.id == 1 { Ok("alice@example.com".to_string()) } else { Err("No email found") }}
fn main() { let email = find_user(1) .and_then(get_user_email);
println!("{:?}", email); // Ok("alice@example.com")
let email = find_user(2) .and_then(get_user_email);
println!("{:?}", email); // Err("User not found")}5. or 和 or_else - 提供备选Result
Section titled “5. or 和 or_else - 提供备选Result”or 和 or_else 在 Err 时返回备选的 Result:
方法签名:
pub fn or(self, res: Result<T, E>) -> Result<T, E>pub fn or_else<U, F>(self, op: F) -> Result<T, E>where F: FnOnce(E) -> Result<T, E>,示例:
fn main() { let ok_result: Result<i32, &str> = Ok(42); let err_result: Result<i32, &str> = Err("error");
// or - 直接返回备选 let result = err_result.or(Ok(0)); println!("{:?}", result); // Ok(0)
let result = ok_result.or(Ok(0)); println!("{:?}", result); // Ok(42) - Ok 穿透
// or_else - 惰性求值 let result = err_result.or_else(|_| { println!("Computing fallback..."); Ok(0) }); println!("{:?}", result); // Ok(0)
let result = ok_result.or_else(|_| { println!("Computing fallback..."); Ok(0) }); println!("{:?}", result); // Ok(42) - 不打印 "Computing fallback..."}6. from - From trait 实现
Section titled “6. from - From trait 实现”From trait 提供了类型间转换的便捷方式:
use std::convert::From;
#[derive(Debug)]struct MyError { message: String,}
impl From<std::io::Error> for MyError { fn from(err: std::io::Error) -> Self { MyError { message: err.to_string(), } }}
impl From<std::num::ParseIntError> for MyError { fn from(err: std::num::ParseIntError) -> Self { MyError { message: err.to_string(), } }}
fn read_and_parse() -> Result<i32, MyError> { let s = "42"; let num: i32 = s.parse()?; // ParseIntError -> MyError 自动转换 Ok(num)}
fn main() { let result = read_and_parse(); println!("{:?}", result); // Ok(42)}7. try_from 和 TryFrom - 尝试转换
Section titled “7. try_from 和 TryFrom - 尝试转换”TryFrom 和 TryInto 用于可能失败的转换:
use std::convert::TryFrom;use std::convert::TryInto;
#[derive(Debug, PartialEq)]struct Positive(i32);
impl TryFrom<i32> for Positive { type Error = &'static str;
fn try_from(value: i32) -> Result<Self, Self::Error> { if value > 0 { Ok(Positive(value)) } else { Err("Value must be positive") } }}
fn main() { // TryFrom let result = Positive::try_from(42); println!("{:?}", result); // Ok(Positive(42))
let result = Positive::try_from(-5); println!("{:?}", result); // Err("Value must be positive")
// TryInto let value: Result<Positive, _> = 42.try_into(); println!("{:?}", value); // Ok(Positive(42))
let value: Result<Positive, _> = (-10).try_into(); println!("{:?}", value); // Err("Value must be positive")}8. ? 操作符 - 错误传播
Section titled “8. ? 操作符 - 错误传播”? 是 Rust 中最常用的错误处理语法糖:
use std::fs::File;use std::io::{self, Read};
fn read_username() -> Result<String, io::Error> { let mut file = File::open("hello.txt")?; let mut contents = String::new(); file.read_to_string(&mut contents)?; Ok(contents)}
fn main() { match read_username() { Ok(s) => println!("Username: {}", s), Err(e) => println!("Error: {}", e), }}等价于:
fn read_username() -> Result<String, io::Error> { let mut file = match File::open("hello.txt") { Ok(f) => f, Err(e) => return Err(e), };
let mut contents = String::new(); match file.read_to_string(&mut contents) { Ok(_) => {} Err(e) => return Err(e), }
Ok(contents)}9. 实战示例:配置验证
Section titled “9. 实战示例:配置验证”#[derive(Debug)]enum ConfigError { MissingHost, InvalidPort(u16), InvalidTimeout(u64),}
impl std::fmt::Display for ConfigError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { ConfigError::MissingHost => write!(f, "Missing host configuration"), ConfigError::InvalidPort(p) => write!(f, "Invalid port: {}", p), ConfigError::InvalidTimeout(t) => write!(f, "Invalid timeout: {}", t), } }}
#[derive(Debug)]struct Config { host: String, port: u16, timeout: u64,}
fn validate_port(port: u16) -> Result<u16, ConfigError> { if port > 0 && port < 65536 { Ok(port) } else { Err(ConfigError::InvalidPort(port)) }}
fn validate_timeout(timeout: u64) -> Result<u64, ConfigError> { if timeout > 0 && timeout <= 300 { Ok(timeout) } else { Err(ConfigError::InvalidTimeout(timeout)) }}
fn build_config(host: Option<String>, port: Option<u16>, timeout: Option<u64>) -> Result<Config, ConfigError> { let host = host.ok_or(ConfigError::MissingHost)?; let port = port.ok_or(ConfigError::InvalidPort(0))?; let port = validate_port(port)?; let timeout = timeout.ok_or(ConfigError::InvalidTimeout(0))?; let timeout = validate_timeout(timeout)?;
Ok(Config { host, port, timeout })}
fn main() { let result = build_config( Some("example.com".to_string()), Some(8080), Some(60), );
println!("{:?}", result); // Ok(Config { host: "example.com", port: 8080, timeout: 60 })
let result = build_config( Some("example.com".to_string()), Some(70000), // 无效端口 Some(60), );
println!("{:?}", result); // Err(InvalidPort(70000))}10. 需要注意的点
Section titled “10. 需要注意的点”-
?操作符要求错误类型兼容:使用?时,错误类型必须能转换为函数返回的Result错误类型。 -
map_errvsand_then:map_err只转换错误,and_then用于需要根据成功值决定下一步操作的场景。 -
FromvsTryFrom:From用于确定能成功的转换,TryFrom用于可能失败的转换。 -
orvsor_else:or_else惰性求值,适合计算成本较高的备选值。 -
自定义错误类型:生产环境中,建议定义自己的错误类型,实现
std::error::Errortrait。
11. 总结
Section titled “11. 总结”Result 是 Rust 错误处理的核心:
| 方法 | 作用 | 返回类型 |
|---|---|---|
map_err | 转换错误类型 | Result<T, F> |
and_then | 链式 Result 操作 | Result<U, E> |
or | Err 时返回备选 | Result<T, E> |
or_else | Err 时惰性返回备选 | Result<T, E> |
From | 类型转换(自动) | Result<T, E> |
TryFrom | 尝试类型转换 | Result<T, E> |
? 操作符是错误处理的最常用语法糖,应该成为你的首选。