Skip to content

Ch 3: std::result - Result错误处理

Result<T, E> 是 Rust 中用于表示操作结果的标准枚举类型,特别适合处理可能失败的操作(如 I/O、解析、计算等)。与 Option 不同,Result 可以携带错误信息。本章将详细讲解 Result 的常用方法。

pub enum Result<T, E> {
Ok(T),
Err(E),
}

Result 是 Rust 错误处理的核心,与 Option 一样都是类型安全的。

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(())
}

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")
}

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..."
}

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)
}

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")
}

? 是 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)
}
#[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))
}
  1. ? 操作符要求错误类型兼容:使用 ? 时,错误类型必须能转换为函数返回的 Result 错误类型。

  2. map_err vs and_then:map_err 只转换错误,and_then 用于需要根据成功值决定下一步操作的场景。

  3. From vs TryFrom:From 用于确定能成功的转换,TryFrom 用于可能失败的转换。

  4. or vs or_else:or_else 惰性求值,适合计算成本较高的备选值。

  5. 自定义错误类型:生产环境中,建议定义自己的错误类型,实现 std::error::Error trait。

Result 是 Rust 错误处理的核心:

方法作用返回类型
map_err转换错误类型Result<T, F>
and_then链式 Result 操作Result<U, E>
orErr 时返回备选Result<T, E>
or_elseErr 时惰性返回备选Result<T, E>
From类型转换(自动)Result<T, E>
TryFrom尝试类型转换Result<T, E>

? 操作符是错误处理的最常用语法糖,应该成为你的首选。