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错误处理的新视角
本文讨论了Rust中不同的错误处理模式,包括panic、使用Option和Result、以及默认恢复机制,并提出了一种超越简单传播或恢复的错误处理方法。
自定义错误在我的Rust应用中是必不可少的
一篇提倡在Rust应用中使用自定义错误类型的博客文章,解释了如何使用map_err和From trait创建一个统一的AppError枚举,以简化不同子系统间的错误处理。
Rust 中的进行中工作
本文介绍了一种在 Rust 开发过程中延迟错误处理的技术和库,允许开发者临时将错误降级为警告,从而在保持正确性的同时维持生产力。
不会编译的数据竞争
本文解释了作者如何利用ruxe库中的类型级不相交技术,教会Rust的类型系统拒绝可能导致数据竞争的并行reducer管道。
iddqd:最难的一种不安全Rust
本文介绍了 iddqd,这是一个 Rust 库,它提供了从值中借用键的映射,减少了重复和同步问题。本文讨论了编写不安全 Rust 代码的挑战以及该库如何保持正确性。