目录
- Rust unwrap_or_else() 使用详解
- 3. 它等价于什么?
- 4. 字符串解析示例
- 5. 文件读取失败时返回默认内容
- 6. 根据不同错误返回不同值
- 7. 提取为普通函数
- 8. Option::unwrap_or_else()
- 10. Result 与 Option 的区别
- 11. unwrap_or() 和 unwrap_or_else() 的核心区别
- 13. 什么情况下应该使用 unwrap_or_else()?
- 14. unwrap_or_else() 会消耗原值
- 15. 闭包为什么是 FnOnce?
- 16. 常见编译错误:闭包参数写错
- 17. 常见编译错误:闭包返回类型不一致
- 18. unwrap_or_else() 与 or_else() 不一样
- 19. unwrap_or_else() 与 ? 的区别
- 20. unwrap_or_default()
- 21. map_or_else() 与 unwrap_or_else() 的区别
- 22. Web 项目中的实际示例
- 23. 数据库查询场景示例
- 24. 方法对比总结
- 25. 最重要的记忆口诀
Rust unwrap_or_else() 使用详解
unwrap_or_else() 用于从 Result 或 Option 中取出内部值:
Result是Ok(value):直接返回valueResult是Err(error):执行闭包,根据错误计算一个备用值Option是Some(value):直接返回valueOption是None:执行闭包,计算一个备用值
它最大的特点是:备用值采用惰性计算,只有真正需要时才执行闭包。 (Rust 文档)
1. Result::unwrap_or_else()
方法签名:
pub fn unwrap_or_else<F>(self, op: F) -> T
where
F: FnOnce(E) -> T,
假设原来的类型是:
Result<T, E>
那么传入的闭包:
|error: E| -> T
会接收错误值 E,并且必须返回一个正常值 T。 (Rust 文档)
基本结构:
let value = result.unwrap_or_else(|error| {
// 处理 error
// 返回一个 T 类型的备用值
});
2. 最基本的示例
fn main() {
let result: Result<i32, &str> = Ok(100);
let value = result.unwrap_or_else(|error| {
println!("发生错误:{error}");
0
});
println!("value = {value}");
}
输出:
value = 100
因为 result 是:
Ok(100)
所以闭包不会执行,直接返回 100。
换成错误:
fn main() {
let result: Result<i32, &str> = Err("数据读取失败");
let value = result.unwrap_or_else(|error| {
println!("发生错误:{error}");
0
});
println!("value = {value}");
}
输出:
发生错误:数据读取失败 value = 0
执行流程是:
Err("数据读取失败")
↓
执行闭包 |error| { ... }
↓
闭包返回 0
↓
value = 0
3. 它等价于什么?
下面的代码:
let value = result.unwrap_or_else(|error| {
println!("错误:{error}");
0
});
基本等价于:
let value = match result {
Ok(value) => value,
Err(error) => {
println!("错误:{error}");
0
}
};
所以可以把 unwrap_or_else() 理解成:
对一个
Result进行简化版match,当出现错误时,通过闭包生成备用值。
不过,当错误处理逻辑比较复杂时,直接使用 match 往往更加清晰。
4. 字符串解析示例
parse() 返回的是 Result:
fn main() {
let text = "123";
let number = text.parse::<i32>().unwrap_or_else(|error| {
println!("数字解析失败:{error}");
0
});
println!("number = {number}");
}
输出:
number = 123
当字符串无法解析时:
fn main() {
let text = "abc";
let number = text.parse::<i32>().unwrap_or_else(|error| {
println!("数字解析失败:{error}");
0
});
println!("number = {number}");
}
输出类似:
数字解析失败:invalid digit found in string number = 0
这里:
text.parse::<i32>()
返回:
Result<i32, ParseIntError>
因此闭包参数 error 的类型就是:
ParseIntError
闭包最终必须返回一个 i32:
|error| {
println!("{error}");
0
}
5. 文件读取失败时返回默认内容
use std::fs;
fn main() {
let content = fs::read_to_string("config.toml").unwrap_or_else(|error| {
println!("读取配置文件失败:{error}");
String::from(
r#"
server_port = 8080
debug = false
"#,
)
});
println!("配置内容:\n{content}");
}
fs::read_to_string() 返回:
Result<String, std::io::Error>
因此:
unwrap_or_else(|error| {
// 必须返回 String
})
如果文件读取成功,直接返回文件内容;如果读取失败,执行闭包并返回默认配置字符串。
6. 根据不同错误返回不同值
因为 Result::unwrap_or_else() 会把错误值传给闭包,所以可以判断错误类型。
use std::fs;
use std::io::ErrorKind;
fn main() {
let content = fs::read_to_string("config.toml").unwrap_or_else(|error| {
match error.kind() {
ErrorKind::NotFound => {
println!("配置文件不存在,使用默认配置");
String::from("port = 8080")
}
ErrorKind::PermissionDenied => {
println!("没有权限读取配置文件");
String::from("port = 9090")
}
_ => {
println!("其他读取错误:{error}");
String::new()
}
}
});
println!("{content}");
}
不过这里需要注意:无论发生哪种错误,最终都必须返回一个 String。
7. 提取为普通函数
闭包也可以替换成普通函数。
use std::num::ParseIntError;
fn parse_error_handler(error: ParseIntError) -> i32 {
println!("解析失败:{error}");
0
}
fn main() {
let result = "abc".parse::<i32>();
let value = result.unwrap_or_else(parse_error_handler);
println!("value = {value}");
}
等价于:
let value = result.unwrap_or_else(|error| {
parse_error_handler(error)
});
普通函数的参数和返回值必须符合:
fn(E) -> T
这里就是:
fn(ParseIntError) -> i32
8. Option::unwrap_or_else()
Option 也有 unwrap_or_else(),但它的闭包形式与 Result 不同。
方法签名:
pub fn unwrap_or_else<F>(self, f: F) -> T
where
F: FnOnce() -> T,
因为 None 内部没有错误值,所以闭包不接收参数。 (Rust 文档)
let value = option.unwrap_or_else(|| {
// 返回一个 T
});
注意这里是:
||
而不是:
|error|
9. Option 基本示例
fn main() {
let value: Option<i32> = Some(100);
let number = value.unwrap_or_else(|| {
println!("Option 是 None");
0
});
println!("number = {number}");
}
输出:
number = 100
因为是 Some(100),闭包不会执行。
当它是 None:
fn main() {
let value: Option<i32> = None;
let number = value.unwrap_or_else(|| {
println!("Option 是 None");
0
});
println!("number = {number}");
}
输出:
Option 是 None number = 0
10. Result 与 Option 的区别
Result
let value = result.unwrap_or_else(|error| {
println!("{error}");
0
});
闭包接收错误:
|error|
Option
let value = option.unwrap_or_else(|| {
0
});
闭包没有参数:
||
对比:
| 类型 | 成功情况 | 失败情况 | 闭包形式 |
|---|---|---|---|
Result<T, E> |
Ok(T) |
Err(E) |
\|error\| -> T |
Option<T> |
Some(T) |
None |
\|\| -> T |
这是使用时最容易混淆的地方。
11. unwrap_or() 和 unwrap_or_else() 的核心区别
这两个方法都可以在失败时返回默认值。
使用 unwrap_or()
let result: Result<i32, &str> = Ok(100); let value = result.unwrap_or(0);
使用 unwrap_or_else()
let result: Result<i32, &str> = Ok(100); let value = result.unwrap_or_else(|_| 0);
在这个简单例子里,两者效果相同。
但是它们的执行时机不同:
unwrap_or(default):default会提前求值unwrap_or_else(|| default):只有失败时才执行闭包
Rust 官方文档明确说明,unwrap_or() 的参数采用立即求值;当默认值需要调用函数计算时,更适合使用惰性求值的 unwrap_or_else()。 (Rust 文档)
12. 惰性执行示例
fn create_default_value() -> String {
println!("正在创建默认值");
String::from("默认配置")
}
fn main() {
let result: Result<String, &str> = Ok(String::from("正式配置"));
let value = result.unwrap_or(create_default_value());
println!("value = {value}");
}
即使 result 是 Ok,也会先执行:
create_default_value()
输出:
正在创建默认值 value = 正式配置
虽然默认值最终没被使用,但函数还是执行了。
改成:
fn create_default_value() -> String {
println!("正在创建默认值");
String::from("默认配置")
}
fn main() {
let result: Result<String, &str> = Ok(String::from("正式配置"));
let value = result.unwrap_or_else(|_| create_default_value());
println!("value = {value}");
}
输出:
value = 正式配置
此时:
create_default_value()
没有执行。
只有 result 是 Err 时才会执行:
let result: Result<String, &str> = Err("读取失败");
输出:
正在创建默认值 value = 默认配置
13. 什么情况下应该使用 unwrap_or_else()?
当默认值需要通过某些操作计算时,适合使用它,例如:
result.unwrap_or_else(|error| {
load_default_config()
});
常见场景包括:
// 调用函数
option.unwrap_or_else(load_default_value);
// 创建复杂对象
option.unwrap_or_else(|| {
User {
id: 0,
name: String::from("游客"),
}
});
// 读取备用文件
result.unwrap_or_else(|_| {
std::fs::read_to_string("default.toml")
.unwrap_or_default()
});
// 根据错误选择备用值
result.unwrap_or_else(|error| {
match error.kind() {
std::io::ErrorKind::NotFound => String::from("文件不存在"),
_ => String::from("未知错误"),
}
});
如果默认值只是一个简单常量:
0 false "unknown"
直接使用 unwrap_or() 通常更清晰:
result.unwrap_or(0); option.unwrap_or(false);
14. unwrap_or_else() 会消耗原值
unwrap_or_else() 的第一个参数是:
self
而不是:
&self
因此它会取得 Result 或 Option 的所有权。
fn main() {
let result: Result<String, &str> = Ok(String::from("hello"));
let value = result.unwrap_or_else(|_| String::from("default"));
println!("{value}");
// 编译错误:result 已经被消费
// println!("{result:?}");
}
如果还需要继续使用原来的 Result,可以先借用:
fn main() {
let result: Result<String, &str> = Ok(String::from("hello"));
let value: &str = result
.as_deref()
.unwrap_or_else(|_| "default");
println!("value = {value}");
println!("result = {result:?}");
}
这里:
result.as_deref()
会产生类似:
Result<&str, &str>
随后被消费的是这个临时 Result,原来的 result 还可以继续使用。
也可以使用:
let value = result
.as_ref()
.unwrap_or_else(|_| &default_value);
15. 闭包为什么是 FnOnce?
签名中有:
F: FnOnce(E) -> T
或者对于 Option:
F: FnOnce() -> T
FnOnce 表示闭包最多调用一次。
这是合理的,因为一个 Result 只可能是:
Ok(...)
或者:
Err(...)
错误处理闭包最多执行一次。
FnOnce 还允许闭包把捕获的变量移动出去:
fn main() {
let default_name = String::from("默认用户");
let result: Result<String, &str> = Err("查询失败");
let name = result.unwrap_or_else(|_| {
default_name
});
println!("{name}");
}
闭包把:
default_name
的所有权作为返回值移了出去。
更简洁地写:
let name = result.unwrap_or_else(|_| default_name);
不过 default_name 会被移动进闭包,因此后面不能再使用。
16. 常见编译错误:闭包参数写错
Result 必须接收一个参数
错误写法:
let result: Result<i32, &str> = Err("error");
let value = result.unwrap_or_else(|| 0);
原因是 Result 的闭包必须接收错误值:
|error|
正确写法:
let value = result.unwrap_or_else(|error| {
println!("{error}");
0
});
不关心错误值时,可以使用 _:
let value = result.unwrap_or_else(|_| 0);
Option 不能接收错误参数
错误写法:
let value: Option<i32> = None; let number = value.unwrap_or_else(|error| 0);
因为 None 中没有错误值。
正确写法:
let number = value.unwrap_or_else(|| 0);
记忆方式:
Result 失败时有 Err(error) → |error| Option 失败时只有 None → ||
17. 常见编译错误:闭包返回类型不一致
let result: Result<i32, &str> = Err("error");
let value = result.unwrap_or_else(|error| {
println!("{error}");
String::from("0")
});
这里会报错。
因为原来的正常值类型是:
i32
所以闭包也必须返回:
i32
不能返回 String。
正确写法:
let value = result.unwrap_or_else(|error| {
println!("{error}");
0
});
规则是:
Result<T, E>::unwrap_or_else(|E| -> T)
前面的 T 与闭包返回的 T 必须一致。
18. unwrap_or_else() 与 or_else() 不一样
这两个方法名称很像,但返回值完全不同。
unwrap_or_else()
从 Result 中取出普通值:
Result<T, E> -> T
示例:
let result: Result<i32, &str> = Err("error");
let value: i32 = result.unwrap_or_else(|_| 0);
最终得到:
i32
错误信息已经被处理掉了。
or_else()
失败时返回另一个 Result:
Result<T, E> -> Result<T, F>
示例:
fn read_primary() -> Result<String, &'static str> {
Err("主配置读取失败")
}
fn read_backup() -> Result<String, &'static str> {
Ok(String::from("备用配置"))
}
fn main() {
let result = read_primary().or_else(|error| {
println!("{error}");
read_backup()
});
println!("{result:?}");
}
结果:
Ok("备用配置")
官方定义中,or_else() 在 Err 时调用闭包,而闭包返回另一个 Result;unwrap_or_else() 则在 Err 时计算并返回内部正常值 T。 (Rust 文档)
对比:
// 返回 String
let value: String =
read_primary().unwrap_or_else(|_| String::from("默认配置"));
// 返回 Result<String, E>
let result: Result<String, _> =
read_primary().or_else(|_| read_backup());
19. unwrap_or_else() 与 ? 的区别
unwrap_or_else()
错误发生时,不再向上传播错误,而是生成一个正常值:
fn get_port() -> u16 {
"abc".parse::<u16>().unwrap_or_else(|error| {
println!("端口解析失败:{error}");
8080
})
}
这个函数返回:
u16
错误被转换成了默认值 8080。
?
错误发生时,将错误返回给调用者:
use std::num::ParseIntError;
fn get_port() -> Result<u16, ParseIntError> {
let port = "abc".parse::<u16>()?;
Ok(port)
}
这个函数返回:
Result<u16, ParseIntError>
选择原则:
错误可以合理降级为默认值
→ unwrap_or_else()
错误应该交给上层处理
→ ?
错误需要详细分类处理
→ match
失败后尝试另一个可能失败的操作
→ or_else()
Rust 官方文档通常建议:可恢复错误优先使用 ?、模式匹配或备用值方法,而不是无条件调用可能引发 panic 的 unwrap()。 (Rust 文档)
20. unwrap_or_default()
如果类型实现了 Default,可以直接使用默认值:
fn main() {
let result: Result<i32, &str> = Err("error");
let value = result.unwrap_or_default();
println!("{value}");
}
i32::default() 是:
0
常见默认值:
i32::default() // 0 bool::default() // false String::default() // "" Vec::<i32>::default() // []
因此:
let value: String = result.unwrap_or_default();
基本等价于:
let value = result.unwrap_or_else(|_| String::default());
或者:
let value = result.unwrap_or_else(|_| String::new());
对于 Option,官方文档也规定:当值为 None 时,unwrap_or_default() 返回该类型的 Default::default()。 (Rust 文档)
21. map_or_else() 与 unwrap_or_else() 的区别
unwrap_or_else() 要求最终结果类型与原始内部值类型相同。
例如:
let result: Result<i32, &str> = Ok(100); let value: i32 = result.unwrap_or_else(|_| 0);
不能直接把 i32 转成 String。
如果成功和失败分支都需要转换为另一种类型,可以使用 map_or_else():
fn main() {
let result: Result<i32, &str> = Ok(100);
let message = result.map_or_else(
|error| format!("处理失败:{error}"),
|value| format!("处理成功:{value}"),
);
println!("{message}");
}
这里两个闭包分别处理:
Err(error) → String Ok(value) → String
最终返回:
String
简单理解:
unwrap_or_else:
Ok(T) 直接返回 T
Err(E) 通过闭包生成 T
map_or_else:
Ok(T) 通过闭包转换成 U
Err(E) 也通过闭包转换成 U
22. Web 项目中的实际示例
例如从环境变量读取服务端口:
use std::env;
fn get_server_port() -> u16 {
env::var("SERVER_PORT")
.unwrap_or_else(|error| {
eprintln!("未读取到 SERVER_PORT:{error}");
String::from("8080")
})
.parse::<u16>()
.unwrap_or_else(|error| {
eprintln!("SERVER_PORT 格式错误:{error}");
8080
})
}
fn main() {
let port = get_server_port();
println!("服务器端口:{port}");
}
这里出现了两次 unwrap_or_else()。
第一次:
env::var("SERVER_PORT")
返回:
Result<String, VarError>
失败时返回默认字符串:
String::from("8080")
第二次:
.parse::<u16>()
返回:
Result<u16, ParseIntError>
失败时返回默认数字:
8080
最终执行逻辑:
读取 SERVER_PORT
│
├─ 成功 → 使用环境变量字符串
│
└─ 失败 → 使用 "8080"
│
↓
解析成 u16
│
├─ 成功 → 返回端口
└─ 失败 → 返回 8080
23. 数据库查询场景示例
假设数据库查询结果是:
Result<Option<User>, DbErr>
需要注意,外层 Result 和内层 Option 是两回事:
#[derive(Debug)]
struct User {
id: u64,
name: String,
}
fn find_user() -> Result<Option<User>, &'static str> {
Ok(None)
}
fn main() {
let user_option = find_user().unwrap_or_else(|error| {
eprintln!("查询用户失败:{error}");
None
});
let user = user_option.unwrap_or_else(|| User {
id: 0,
name: String::from("游客"),
});
println!("{user:?}");
}
第一层处理:
Result<Option<User>, &str>
所以:
unwrap_or_else(|error| None)
闭包必须返回:
Option<User>
第二层处理:
Option<User>
所以:
unwrap_or_else(|| User { ... })
闭包必须返回:
User
不过真实业务中,数据库连接失败通常不应该简单地变成“用户不存在”。更合理的方式一般是保留并传播数据库错误:
fn get_user() -> Result<User, &'static str> {
let user_option = find_user()?;
let user = user_option.unwrap_or_else(|| User {
id: 0,
name: String::from("游客"),
});
Ok(user)
}
这里:
- 数据库错误通过
?向上传播 - 查询成功但没有数据时,才使用默认用户
24. 方法对比总结
| 方法 | 失败时行为 | 返回类型 | 是否可能 panic |
|---|---|---|---|
unwrap() |
直接 panic | T |
是 |
expect("信息") |
带自定义信息 panic | T |
是 |
unwrap_or(value) |
返回提前准备好的默认值 | T |
否 |
unwrap_or_else(fn) |
执行闭包生成默认值 | T |
闭包本身可能 panic |
unwrap_or_default() |
返回 T::default() |
T |
否 |
or_else(fn) |
返回另一个容器 | Result<T, F> / Option<T> |
否 |
map_or_else() |
成功和失败都转换 | U |
否 |
? |
将错误向上传播 | 取出 T,函数返回容器 |
否 |
25. 最重要的记忆口诀
// Result:Err 中有错误值,所以闭包有一个参数
result.unwrap_or_else(|error| {
default_value
});
// 不使用错误值
result.unwrap_or_else(|_| {
default_value
});
// Option:None 中没有错误值,所以闭包没有参数
option.unwrap_or_else(|| {
default_value
});
选择方法时:
简单固定默认值 → unwrap_or() 需要计算默认值 → unwrap_or_else() 使用类型默认值 → unwrap_or_default() 错误继续向上传递 → ? 失败后尝试备用操作 → or_else() 成功和失败都要转换 → map_or_else() 复杂分支处理 → match
最典型、最常用的写法是:
let port = text.parse::<u16>().unwrap_or_else(|error| {
eprintln!("端口解析失败:{error},使用默认端口 8080");
8080
});