不灭的焱

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

作者:AlbertWen  添加时间:2026-07-19 01:18:42  修改时间:2026-07-22 13:06:14  分类:01.Rust编程  编辑

目录

Rust unwrap_or_else() 使用详解

unwrap_or_else() 用于从 ResultOption 中取出内部值:

  • ResultOk(value):直接返回 value
  • ResultErr(error):执行闭包,根据错误计算一个备用值
  • OptionSome(value):直接返回 value
  • OptionNone:执行闭包,计算一个备用值

它最大的特点是:备用值采用惰性计算,只有真正需要时才执行闭包。 (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}");
}

即使 resultOk,也会先执行:

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

没有执行。

只有 resultErr 时才会执行:

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

因此它会取得 ResultOption 的所有权。

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 时调用闭包,而闭包返回另一个 Resultunwrap_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
});