不灭的焱

革命尚未成功,同志仍须努力 下载Java21

作者:AlbertWen  添加时间:2026-07-19 22:39:54  修改时间:2026-07-25 20:58:09  分类:01.Rust编程  编辑

目录

Rust ok_or_else() 使用详解

ok_or_else()Option<T> 提供的方法,用于把:

Option<T>

转换为:

Result<T, E>

它的核心规则是:

Some(value) → Ok(value)
None        → 执行闭包,生成 Err(error)

最常见的用途是:

OptionNone 时,将“没有值”转换成一个具体的业务错误,然后配合 ? 把错误向上传递。

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>

其中:

  • TSome 中保存的值类型
  • E 是闭包生成的错误类型
  • 闭包 || -> E 不接收参数
  • 只有当 OptionNone 时,闭包才会执行

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() 理解为:

使用简洁的方式,把 OptionNone 分支转换成 ResultErr 分支。

对应关系是:

原始 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 的 ? 不会自动在 OptionResult 之间相互转换;需要先使用 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

只有当 nameNone 时,才会执行:

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

当原来的 OptionNone 时,返回另一个 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> 分别转换 SomeNone 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 错误。