目录
- Rust ok_or_else() 使用详解
- 1. 方法签名
- 2. 最基本的示例
- 3. 它等价于什么?
- 4. 为什么闭包是 ||?
- 5. 使用 String 作为错误类型
- 6. 最常见的用法:配合 ?
- 7. ok_or() 与 ok_or_else() 的区别
- 8. 立即求值与惰性求值
- 9. 简单错误值使用哪个?
- 10. 使用自定义错误枚举
- 11. 为自定义错误实现 Display
- 12. 从集合中查找数据
- 13. 从 HashMap 中读取数据
- 14. 读取结构体中的可选字段
- 15. ok_or_else() 会消耗 Option
- 16. 如何避免移动原来的值?
- 17. 闭包可以捕获外部变量
- 18. 为什么使用 FnOnce?
- 19. 常见错误:在 Result 上调用 ok_or_else()
- 20. 常见错误:闭包写成带参数
- 21. 常见错误:错误类型推断不明确
- 22. ok_or_else() 与 unwrap_or_else() 的区别
- 23. ok_or_else() 与 or_else() 的区别
- 24. ok_or_else() 与 map_or_else() 的区别
- 25. 数据库查询场景
- 26. 配置读取场景
- 27. Web 接口参数校验示例
- 28. 方法对比总结
- 29. 使用选择口诀
- 30. 最典型的写法
Rust ok_or_else() 使用详解
ok_or_else() 是 Option<T> 提供的方法,用于把:
Option<T>
转换为:
Result<T, E>
它的核心规则是:
Some(value) → Ok(value) None → 执行闭包,生成 Err(error)
最常见的用途是:
当
Option为None时,将“没有值”转换成一个具体的业务错误,然后配合?把错误向上传递。
Rust 官方文档将其定义为:把 Some(v) 转换成 Ok(v),当值为 None 时,调用闭包生成 Err。(Rust 文档)
1. 方法签名
简化后的方法签名如下:
pub fn ok_or_else<E, F>(self, err: F) -> Result<T, E>
where
F: FnOnce() -> E,
假设原始值是:
Option<T>
调用:
option.ok_or_else(|| error)
最终会得到:
Result<T, E>
其中:
T是Some中保存的值类型E是闭包生成的错误类型- 闭包
|| -> E不接收参数 - 只有当
Option为None时,闭包才会执行
2. 最基本的示例
Some 转换成 Ok
fn main() {
let option: Option<i32> = Some(100);
let result: Result<i32, &str> =
option.ok_or_else(|| "没有找到数据");
println!("{result:?}");
}
输出:
Ok(100)
因为原来的值是:
Some(100)
所以直接转换成:
Ok(100)
传给 ok_or_else() 的闭包不会执行。
None 转换成 Err
fn main() {
let option: Option<i32> = None;
let result: Result<i32, &str> =
option.ok_or_else(|| "没有找到数据");
println!("{result:?}");
}
输出:
Err("没有找到数据")
执行过程如下:
None
↓
执行 || "没有找到数据"
↓
生成错误值
↓
Err("没有找到数据")
3. 它等价于什么?
下面的代码:
let result = option.ok_or_else(|| "数据不存在");
基本等价于:
let result = match option {
Some(value) => Ok(value),
None => Err("数据不存在"),
};
因此,可以将 ok_or_else() 理解为:
使用简洁的方式,把
Option的None分支转换成Result的Err分支。
对应关系是:
原始 Option |
转换后的 Result |
|---|---|
Some(value) |
Ok(value) |
None |
Err(error) |
4. 为什么闭包是 ||?
ok_or_else() 的闭包写法是:
||
例如:
option.ok_or_else(|| "数据不存在")
而不是:
option.ok_or_else(|error| "数据不存在")
原因是 None 内部没有任何值:
None
它不像 Result::Err(error) 那样携带错误信息。
所以 ok_or_else() 无法向闭包传递参数,闭包只能自行创建错误:
|| "数据不存在"
记忆方式:
Option::None 没有内部数据 所以闭包没有参数:||
5. 使用 String 作为错误类型
实际项目中,经常需要动态生成错误信息。
fn main() {
let user_id = 1001;
let user: Option<String> = None;
let result = user.ok_or_else(|| {
format!("用户不存在,用户 ID:{user_id}")
});
println!("{result:?}");
}
输出:
Err("用户不存在,用户 ID:1001")
这里原来的类型是:
Option<String>
调用后得到:
Result<String, String>
类型转换过程:
Option<String>
↓
Result<String, String>
第一个 String 是成功值类型,第二个 String 是错误值类型。
6. 最常见的用法:配合 ?
ok_or_else() 最有价值的地方,是将 Option 转换成 Result 后继续使用 ?。
例如,根据 ID 查找用户:
#[derive(Debug)]
struct User {
id: u64,
name: String,
}
fn find_user(user_id: u64) -> Option<User> {
if user_id == 1001 {
Some(User {
id: 1001,
name: String::from("张三"),
})
} else {
None
}
}
fn get_user(user_id: u64) -> Result<User, String> {
let user = find_user(user_id)
.ok_or_else(|| format!("用户不存在,用户 ID:{user_id}"))?;
Ok(user)
}
fn main() {
match get_user(2001) {
Ok(user) => println!("查询成功:{user:?}"),
Err(error) => println!("查询失败:{error}"),
}
}
输出:
查询失败:用户不存在,用户 ID:2001
重点代码:
let user = find_user(user_id)
.ok_or_else(|| format!("用户不存在,用户 ID:{user_id}"))?;
它可以分为两步理解:
let result = find_user(user_id)
.ok_or_else(|| format!("用户不存在,用户 ID:{user_id}"));
let user = result?;
执行逻辑:
find_user(user_id)
│
├─ Some(user)
│ ↓
│ Ok(user)
│ ↓
│ ? 取出 user
│
└─ None
↓
创建错误信息
↓
Err(String)
↓
? 提前返回错误
Rust 的 ? 不会自动在 Option 和 Result 之间相互转换;需要先使用 ok_or() 或 ok_or_else() 明确完成转换。(Rust 文档)
7. ok_or() 与 ok_or_else() 的区别
这两个方法都可以把:
Option<T>
转换成:
Result<T, E>
区别在于错误值的创建时机。
ok_or()
直接传入错误值:
let result = option.ok_or("数据不存在");
方法逻辑:
Some(value) → Ok(value)
None → Err("数据不存在")
ok_or_else()
通过闭包创建错误值:
let result = option.ok_or_else(|| "数据不存在");
方法逻辑:
Some(value) → Ok(value),不执行闭包 None → 执行闭包并生成 Err
官方文档指出,ok_or() 的错误参数会立即求值;当错误值需要通过函数调用计算时,更适合使用惰性求值的 ok_or_else()。(Rust 文档)
8. 立即求值与惰性求值
看下面的示例:
fn create_error() -> String {
println!("正在创建错误信息");
String::from("数据不存在")
}
fn main() {
let option = Some(100);
let result = option.ok_or(create_error());
println!("{result:?}");
}
输出:
正在创建错误信息 Ok(100)
即使 option 是:
Some(100)
不需要错误值,create_error() 还是执行了。
因为传给 ok_or() 的参数会提前计算:
create_error()
改成 ok_or_else():
fn create_error() -> String {
println!("正在创建错误信息");
String::from("数据不存在")
}
fn main() {
let option = Some(100);
let result = option.ok_or_else(create_error);
println!("{result:?}");
}
输出:
Ok(100)
此时 create_error() 不会执行。
只有当值是 None 时:
let option: Option<i32> = None;
才会输出:
正在创建错误信息
Err("数据不存在")
9. 简单错误值使用哪个?
如果错误值只是一个简单的固定值:
"用户不存在"
通常可以直接使用:
option.ok_or("用户不存在")
如果错误值需要动态计算,例如:
format!("用户不存在,用户 ID:{user_id}")
建议使用:
option.ok_or_else(|| {
format!("用户不存在,用户 ID:{user_id}")
})
选择原则:
简单、固定、创建成本很低的错误值
→ ok_or()
需要调用函数或动态构造的错误值
→ ok_or_else()
例如:
// 简单固定错误
let user = option.ok_or("用户不存在")?;
// 动态错误
let user = option.ok_or_else(|| {
format!("用户不存在,ID:{user_id}")
})?;
10. 使用自定义错误枚举
真实项目不建议所有错误都使用字符串,更常见的是定义错误枚举。
#[derive(Debug)]
enum AppError {
UserNotFound {
user_id: u64,
},
MissingConfig {
name: String,
},
}
#[derive(Debug)]
struct User {
id: u64,
name: String,
}
fn find_user(user_id: u64) -> Option<User> {
None
}
fn get_user(user_id: u64) -> Result<User, AppError> {
find_user(user_id).ok_or_else(|| {
AppError::UserNotFound { user_id }
})
}
fn main() {
let result = get_user(1001);
println!("{result:?}");
}
输出:
Err(UserNotFound { user_id: 1001 })
这里的类型转换是:
Option<User>
↓
Result<User, AppError>
错误闭包返回:
AppError::UserNotFound { user_id }
因此错误类型 E 是:
AppError
11. 为自定义错误实现 Display
可以进一步实现 Display,让错误信息更友好。
use std::fmt;
#[derive(Debug)]
enum AppError {
UserNotFound {
user_id: u64,
},
MissingConfig {
name: String,
},
}
impl fmt::Display for AppError {
fn fmt(
&self,
formatter: &mut fmt::Formatter<'_>,
) -> fmt::Result {
match self {
AppError::UserNotFound { user_id } => {
write!(formatter, "用户不存在,用户 ID:{user_id}")
}
AppError::MissingConfig { name } => {
write!(formatter, "缺少配置项:{name}")
}
}
}
}
impl std::error::Error for AppError {}
fn find_user_name(user_id: u64) -> Option<String> {
None
}
fn get_user_name(user_id: u64) -> Result<String, AppError> {
find_user_name(user_id).ok_or_else(|| {
AppError::UserNotFound { user_id }
})
}
fn main() {
match get_user_name(1001) {
Ok(name) => println!("用户名:{name}"),
Err(error) => println!("查询失败:{error}"),
}
}
输出:
查询失败:用户不存在,用户 ID:1001
12. 从集合中查找数据
标准集合的 get() 方法通常返回 Option。
例如从数组中读取指定位置的数据:
fn get_number(
numbers: &[i32],
index: usize,
) -> Result<i32, String> {
numbers
.get(index)
.copied()
.ok_or_else(|| {
format!(
"索引越界:index={index},数组长度={}",
numbers.len()
)
})
}
fn main() {
let numbers = vec![10, 20, 30];
println!("{:?}", get_number(&numbers, 1));
println!("{:?}", get_number(&numbers, 10));
}
输出:
Ok(20)
Err("索引越界:index=10,数组长度=3")
这里:
numbers.get(index)
返回:
Option<&i32>
调用:
.copied()
得到:
Option<i32>
再调用:
.ok_or_else(...)
得到:
Result<i32, String>
完整转换过程:
Option<&i32>
↓ copied()
Option<i32>
↓ ok_or_else()
Result<i32, String>
13. 从 HashMap 中读取数据
HashMap::get() 也会返回 Option。
use std::collections::HashMap;
fn get_config<'a>(
configs: &'a HashMap<String, String>,
name: &str,
) -> Result<&'a str, String> {
configs
.get(name)
.map(String::as_str)
.ok_or_else(|| {
format!("缺少配置项:{name}")
})
}
fn main() {
let configs = HashMap::from([
(
String::from("server_port"),
String::from("8080"),
),
(
String::from("server_host"),
String::from("127.0.0.1"),
),
]);
match get_config(&configs, "database_url") {
Ok(value) => println!("配置值:{value}"),
Err(error) => println!("读取失败:{error}"),
}
}
输出:
读取失败:缺少配置项:database_url
关键过程:
configs.get(name)
返回:
Option<&String>
然后:
.map(String::as_str)
转换成:
Option<&str>
最后:
.ok_or_else(...)
转换成:
Result<&str, String>
14. 读取结构体中的可选字段
假设配置结构体中的某些字段是可选的:
#[derive(Debug)]
struct AppConfig {
database_url: Option<String>,
redis_url: Option<String>,
}
可以使用 ok_or_else() 检查必填配置。
#[derive(Debug)]
struct AppConfig {
database_url: Option<String>,
redis_url: Option<String>,
}
fn get_database_url(
config: &AppConfig,
) -> Result<&str, String> {
config
.database_url
.as_deref()
.ok_or_else(|| {
String::from("未配置 database_url")
})
}
fn main() {
let config = AppConfig {
database_url: None,
redis_url: Some(String::from(
"redis://127.0.0.1:6379",
)),
};
println!("{:?}", get_database_url(&config));
}
输出:
Err("未配置 database_url")
这里使用:
.as_deref()
把:
Option<String>
借用为:
Option<&str>
这样不会移动结构体中的 String。
15. ok_or_else() 会消耗 Option
方法签名中的接收者是:
self
这意味着它会取得 Option 的所有权。
例如:
fn main() {
let option = Some(String::from("hello"));
let result = option.ok_or_else(|| {
String::from("没有值")
});
println!("{result:?}");
// 编译错误:option 已经被移动
// println!("{option:?}");
}
因为 String 没有实现 Copy,所以:
option.ok_or_else(...)
会消费 option。
16. 如何避免移动原来的值?
可以先调用:
as_ref()
把:
Option<T>
转换成:
Option<&T>
示例:
fn main() {
let option = Some(String::from("hello"));
let result = option
.as_ref()
.ok_or_else(|| "没有值");
println!("result = {result:?}");
println!("option = {option:?}");
}
输出:
result = Ok("hello")
option = Some("hello")
此时被消费的是:
Option<&String>
而不是原来的:
Option<String>
对于字符串,还可以使用:
as_deref()
fn main() {
let option = Some(String::from("hello"));
let result: Result<&str, &str> = option
.as_deref()
.ok_or_else(|| "没有值");
println!("{result:?}");
println!("{option:?}");
}
17. 闭包可以捕获外部变量
fn get_user_name(
user_id: u64,
name: Option<String>,
) -> Result<String, String> {
name.ok_or_else(|| {
format!("用户 {user_id} 没有设置姓名")
})
}
fn main() {
let result = get_user_name(1001, None);
println!("{result:?}");
}
闭包捕获了外部变量:
user_id
只有当 name 为 None 时,才会执行:
format!("用户 {user_id} 没有设置姓名")
18. 为什么使用 FnOnce?
方法签名中有:
F: FnOnce() -> E
FnOnce 表示该闭包最多调用一次。
一个 Option 要么是:
Some(value)
要么是:
None
所以生成错误的闭包最多执行一次。
另外,FnOnce 允许闭包移动捕获的变量:
fn main() {
let error_message =
String::from("用户不存在");
let user: Option<String> = None;
let result = user.ok_or_else(|| {
error_message
});
println!("{result:?}");
}
闭包把 error_message 的所有权移动出来,作为错误值返回。
也可以简写为:
let result = user.ok_or_else(|| error_message);
之后不能再使用:
error_message
因为它的所有权已经移动到了闭包中。
19. 常见错误:在 Result 上调用 ok_or_else()
错误写法:
let result: Result<i32, &str> = Ok(100); // 错误:Result 没有 Option::ok_or_else 这种转换需求 let value = result.ok_or_else(|| "没有数据");
ok_or_else() 的主要调用对象是:
Option<T>
因为它的作用是:
Option<T> → Result<T, E>
如果手上已经是 Result,通常应该使用:
map_err() or_else() unwrap_or_else()
具体使用哪个方法,取决于你是要转换错误、恢复错误,还是提取普通值。
20. 常见错误:闭包写成带参数
错误写法:
let option: Option<i32> = None;
let result = option.ok_or_else(|error| {
"数据不存在"
});
这里会报错,因为 ok_or_else() 要求的是:
FnOnce() -> E
也就是没有参数的闭包。
正确写法:
let result = option.ok_or_else(|| {
"数据不存在"
});
记住:
ok_or_else(|| error)
不是:
ok_or_else(|error| error)
21. 常见错误:错误类型推断不明确
有时只写:
let result = Some(100).ok_or_else(|| {
"错误".into()
});
编译器可能无法确定:
"错误".into()
具体要转换成什么类型。
可以明确指定类型:
let result: Result<i32, String> =
Some(100).ok_or_else(|| {
String::from("错误")
});
或者:
let result =
Some(100).ok_or_else(|| -> String {
"错误".into()
});
第一种通常更清晰:
let result: Result<i32, String> = ...
22. ok_or_else() 与 unwrap_or_else() 的区别
这两个方法名称相似,但作用完全不同。
ok_or_else()
把 Option 转换成 Result:
let option: Option<i32> = None;
let result: Result<i32, &str> =
option.ok_or_else(|| "没有数据");
结果是:
Err("没有数据")
返回类型仍然是一个容器:
Result<i32, &str>
错误仍然保留,可以继续传播或处理。
unwrap_or_else()
从 Option 中取出普通值;如果是 None,创建一个默认值:
let option: Option<i32> = None;
let value: i32 =
option.unwrap_or_else(|| 0);
结果是:
0
返回类型是普通值:
i32
None 被降级成了默认值,不再保留错误信息。
对比
| 方法 | 输入 | None 时的处理 |
返回类型 |
|---|---|---|---|
ok_or_else() |
Option<T> |
生成 Err(E) |
Result<T, E> |
unwrap_or_else() |
Option<T> |
生成默认值 T |
T |
示例:
let option: Option<i32> = None;
// 保留失败状态
let result = option.ok_or_else(|| "没有数据");
// Err("没有数据")
let option: Option<i32> = None; // 使用默认值,不保留失败状态 let value = option.unwrap_or_else(|| 0); // 0
选择原则:
None 是一个需要处理的业务错误
→ ok_or_else()
None 可以合理地替换成默认值
→ unwrap_or_else()
23. ok_or_else() 与 or_else() 的区别
ok_or_else()
把 Option 转换成 Result:
let result: Result<i32, &str> =
option.ok_or_else(|| "没有数据");
转换关系:
Option<T> → Result<T, E>
or_else()
当原来的 Option 为 None 时,返回另一个 Option:
let value: Option<i32> =
option.or_else(|| Some(100));
转换关系:
Option<T> → Option<T>
示例:
fn main() {
let primary: Option<i32> = None;
let value = primary.or_else(|| {
println!("主数据不存在,读取备用数据");
Some(100)
});
println!("{value:?}");
}
输出:
主数据不存在,读取备用数据 Some(100)
对比:
ok_or_else()
None → Err(error)
or_else()
None → 另一个 Option
24. ok_or_else() 与 map_or_else() 的区别
map_or_else() 会直接计算最终的普通值。
fn main() {
let option = Some(100);
let message = option.map_or_else(
|| String::from("没有数据"),
|value| format!("数据是:{value}"),
);
println!("{message}");
}
输出:
数据是:100
两者区别:
ok_or_else:
Some(T) → Ok(T)
None → Err(E)
返回 Result<T, E>
map_or_else:
Some(T) → 调用成功闭包生成 U
None → 调用默认闭包生成 U
返回 U
25. 数据库查询场景
很多 ORM 的查询结果可能是:
Result<Option<User>, DbErr>
含义是:
Err(DbErr):数据库查询失败Ok(None):查询成功,但没有找到数据Ok(Some(user)):查询成功,并找到用户
可以使用 ? 和 ok_or_else() 分别处理两层状态:
#[derive(Debug)]
struct User {
id: u64,
name: String,
}
#[derive(Debug)]
enum ServiceError {
DatabaseError(String),
UserNotFound {
user_id: u64,
},
}
fn query_user(
user_id: u64,
) -> Result<Option<User>, String> {
Ok(None)
}
fn get_user(
user_id: u64,
) -> Result<User, ServiceError> {
let user_option = query_user(user_id)
.map_err(ServiceError::DatabaseError)?;
let user = user_option.ok_or_else(|| {
ServiceError::UserNotFound { user_id }
})?;
Ok(user)
}
fn main() {
println!("{:?}", get_user(1001));
}
执行逻辑:
query_user(user_id)
│
├─ Err(db_error)
│ ↓
│ 数据库错误
│
├─ Ok(None)
│ ↓
│ UserNotFound
│
└─ Ok(Some(user))
↓
返回 User
重点是:
let user_option = query_user(user_id)?;
处理外层的数据库查询错误。
然后:
let user = user_option.ok_or_else(|| {
ServiceError::UserNotFound { user_id }
})?;
把内层的“没有查询到用户”转换为业务错误。
26. 配置读取场景
#[derive(Debug)]
enum ConfigError {
MissingField {
field: &'static str,
},
}
#[derive(Debug)]
struct ServerConfig {
host: Option<String>,
port: Option<u16>,
}
fn validate_config(
config: ServerConfig,
) -> Result<(String, u16), ConfigError> {
let host = config.host.ok_or_else(|| {
ConfigError::MissingField {
field: "host",
}
})?;
let port = config.port.ok_or_else(|| {
ConfigError::MissingField {
field: "port",
}
})?;
Ok((host, port))
}
fn main() {
let config = ServerConfig {
host: Some(String::from("127.0.0.1")),
port: None,
};
println!("{:?}", validate_config(config));
}
输出:
Err(MissingField { field: "port" })
这里把配置中的可选字段:
Option<String> Option<u16>
转换成带错误信息的:
Result<String, ConfigError> Result<u16, ConfigError>
这非常适合配置校验、请求参数校验和业务数据校验。
27. Web 接口参数校验示例
#[derive(Debug)]
struct CreateUserRequest {
username: Option<String>,
email: Option<String>,
}
#[derive(Debug)]
enum ValidationError {
MissingField {
field: &'static str,
},
}
fn validate_request(
request: CreateUserRequest,
) -> Result<(String, String), ValidationError> {
let username = request.username.ok_or_else(|| {
ValidationError::MissingField {
field: "username",
}
})?;
let email = request.email.ok_or_else(|| {
ValidationError::MissingField {
field: "email",
}
})?;
Ok((username, email))
}
这里的业务含义是:
username = Some(value)
→ 参数存在,继续执行
username = None
→ 返回 MissingField 错误
28. 方法对比总结
| 方法 | 输入 | 失败时行为 | 返回类型 |
|---|---|---|---|
ok_or(error) |
Option<T> |
使用提前创建的错误 | Result<T, E> |
ok_or_else(fn) |
Option<T> |
执行闭包创建错误 | Result<T, E> |
unwrap_or(value) |
Option<T> |
返回提前创建的默认值 | T |
unwrap_or_else(fn) |
Option<T> |
执行闭包创建默认值 | T |
or_else(fn) |
Option<T> |
执行闭包返回备用 Option |
Option<T> |
map_or_else() |
Option<T> |
分别转换 Some 和 None |
U |
29. 使用选择口诀
Option 的 None 需要变成错误
→ ok_or_else()
错误值很简单,不需要动态计算
→ ok_or()
None 可以使用默认值代替
→ unwrap_or_else()
None 时尝试另一个 Option
→ or_else()
Some 和 None 都需要转换成其他类型
→ map_or_else()
30. 最典型的写法
在真实业务项目中,最常见的形式是:
fn get_user(
user_id: u64,
) -> Result<User, AppError> {
let user = find_user(user_id)
.ok_or_else(|| {
AppError::UserNotFound { user_id }
})?;
Ok(user)
}
可以把这段代码理解成:
查找用户
│
├─ 找到用户
│ Some(user)
│ ↓
│ Ok(user)
│ ↓
│ 继续执行
│
└─ 没找到用户
None
↓
创建 UserNotFound
↓
Err(UserNotFound)
↓
? 提前返回
最核心的一句话是:
ok_or_else()不负责提供默认值,而是负责把Option中的“没有值”转换成一个可以传播和处理的Result错误。