Rust - 在 map 闭包中处理 Result
摘要
本文探讨了在 map 闭包中使用返回 Result 的函数时常见的 Rust 错误,并提供了多种解决方案:收集结果、使用循环、filter_map 和 try_fold。
<p><a href="https://lobste.rs/s/1r09vz/rust_handling_results_map_closure">评论</a></p>
查看缓存全文
缓存时间: 2026/07/04 06:38
# 在 Map 闭包中处理 Result
来源:https://reemus.dev/tldr/rust-handling-results-in-map-closure
你是否遇到过在 Rust 的 `.map()` 闭包中使用返回 `Result` 的函数时报错的问题?我相信这是学习 Rust 时常见的困扰。幸运的是,解决方案其实很简单。
## 问题
考虑以下 Rust 代码,其中有两个函数都返回 `Result` 类型。第一个函数为一行(字符串切片)添加前缀,第二个函数接受一个行列表并使用第一个函数为每行添加前缀。
```rust
use std::error::Error;
fn prefix_line(line: &str) -> Result<String, Box<dyn Error>> {
Ok(format!("PREFIX: {}", line))
}
fn prefix_each(lines: &[&str]) -> Result<Vec<String>, Box<dyn Error>> {
let result = lines
.iter()
.map(|line| prefix_line(line)?)
.collect::<Vec<String>>();
Ok(result)
}
```
rust-icon 尝试编译这段代码会在第 10 行出现错误:
> the '?' operator can only be used in a closure that returns 'Result' or 'Option' (or another type that implements 'FromResidual') [E0277]
> Help: the trait 'FromResidual<Result<Infallible, Box<dyn Error>>>' is not implemented for 'std::string::String'
错误分析:
- `map` 函数接受一个闭包作为参数
- `.map()` 闭包中的 `?` 运算符有问题
- 因为 `?` 运算符期望闭包返回 `Result` 或 `Option`
- 而闭包的返回类型被推断为与 `map` 期望的输出类型匹配,即 `String`
- 由于 `String` 没有实现 `FromResidual`,因此报错
## 解决方案
### 收集结果
我发现解决这个问题最简单的方法:
- 调用 `prefix_line(line)` 时不使用 `?` 运算符,这会输出 `Vec<Result<String, ...>>`
- 使用 `collect::<Result<Vec<_>, _>>()?` 来正确推断类型,收集所有的 `Result` 并在出错时传播错误
- 最终得到我们想要的 `Vec<String>`
```rust
fn prefix_each(lines: &[&str]) -> Result<Vec<String>, Box<dyn Error>> {
let result = lines
.iter()
.map(|line| prefix_line(line))
.collect::<Result<Vec<_>, _>>()?;
Ok(result)
}
```
rust-icon
### 使用循环
显而易见的初始解决方案,但存在缺点:
- 打破了函数式风格
- 需要可变变量
```rust
fn prefix_each(lines: &[&str]) -> Result<Vec<String>, Box<dyn Error>> {
let mut result = Vec::new();
for line in lines {
result.push(prefix_line(line)?);
}
Ok(result)
}
```
rust-icon
### 使用 filter_map
- `filter_map` 会丢弃 `Err` 值,只收集成功的 `Ok` 值
- 如果你希望忽略错误,只关心成功的结果,这个方案很有用
```rust
fn prefix_each(lines: &[&str]) -> Result<Vec<String>, Box<dyn Error>> {
let result = lines
.iter()
.filter_map(|line| prefix_line(line).ok())
.collect::<Vec<String>>();
Ok(result)
}
```
rust-icon
### 使用 try_fold
- 使用 `try_fold` 来聚合结果并优雅地处理错误
- 它允许你直接返回 `Result` 类型
```rust
fn prefix_each(lines: &[&str]) -> Result<String, Box<dyn Error>> {
let result = lines.iter().try_fold(String::new(), |mut acc, line| {
prefix_line(line).map(|prefixed_line| {
acc.push_str(&prefixed_line);
acc.push_str("\n");
acc
})
})?;
Ok(result)
}
```
rust-icon
相似文章
Rust:不要 panic
一段视频转录文本,讨论如何通过使用组合器和 Result 类型而不是 unwrap 来避免 Rust 中的 panic,解释了三种典型的崩溃原因,并强调编写健壮的代码。
Rust错误处理的新视角
本文讨论了Rust中不同的错误处理模式,包括panic、使用Option和Result、以及默认恢复机制,并提出了一种超越简单传播或恢复的错误处理方法。
Rust 错误处理中缺失的一块
作者介绍了一个新的 Rust 错误处理库 eros,用元组形式的"错误集合"(开放和类型 ErrorUnion)替代手写枚举,让错误类型像函数一样自由组合,同时保留编译期对可能错误的精确追踪。
自定义错误在我的Rust应用中是必不可少的
一篇提倡在Rust应用中使用自定义错误类型的博客文章,解释了如何使用map_err和From trait创建一个统一的AppError枚举,以简化不同子系统间的错误处理。
捕获子句作为效果
本文探讨了Rust中显式捕获子句作为move表达式的替代方案,提出了一种将引用转换为所有权值的一等语言特性,并分析了多种闭包捕获模式。