如何在循环访问对 Result 的引用时使用问号运算符?



我需要将向量Vec<Result<SomeStruct>>分成几块,遍历每个片段,做一些工作,如果包含Result<SomeStruct>则返回错误。

enum SomeStruct {}
#[derive(Debug)]
struct SomeError {}
type Result<T> = std::result::Result<T, SomeError>;
fn foo(v: Vec<Result<SomeStruct>>) -> Result<()> {
for chunk in v.chunks(42) {
for value in chunk {
let value = value?;
//do smth with value
}
}
Ok(())
}
fn main() {
let mut values = Vec::new();
foo(values).unwrap();
}

操场

但是,我收到错误

error[E0277]: the `?` operator can only be applied to values that implement `std::ops::Try`
--> src/main.rs:11:25
|
11 |             let value = value?;
|                         ^^^^^^ the `?` operator cannot be applied to type `&std::result::Result<SomeStruct, SomeError>`
|
= help: the trait `std::ops::Try` is not implemented for `&std::result::Result<SomeStruct, SomeError>`
= note: required by `std::ops::Try::into_result`

似乎?期望值本身而不是对该值的共享引用,但我无法取消引用共享引用,也不知道如何获取此值本身,因为chunk这里&[Result<SomeStruct>]value&Result<SomeStruct>

我该如何解决它?

一般情况

你的函数签名要求你返回一个拥有的SomeError,但你所拥有的只是对一个的引用。除非你有办法&SomeError转换为SomeError,否则这个问题是无法解决的。

最适用的解决方案是将您的错误更改为可克隆(或可复制,如果适用),然后通过Result::as_refResult::map_err将您的&Result<T, E>转换为Result<&T, E>

#[derive(Debug, Clone)]
struct SomeError {}
for value in chunk {
let value = value.as_ref().map_err(Clone::clone)?;
// do something with `value`
}

返回对错误的引用也是可能的,但远不常见。这要求错误生存期比调用函数的时间长:

type Result<T, E = SomeError> = std::result::Result<T, E>;
fn foo(v: &[Result<SomeStruct>]) -> Result<(), &SomeError> {
for chunk in v.chunks(42) {
for value in chunk {
let value = value.as_ref()?;
// do something with `value`
}
}
Ok(())
}
fn main() {
let values = Vec::new();
foo(&values).unwrap();
}

另请参阅:

  • 如何借用对期权中内容的引用?
  • 为什么不鼓励接受对字符串(&String)、Vec(&Vec)或Box(&Box)的引用作为函数参数?

具体案例

由于您拥有Vec并且没有利用内部迭代器是一个切片的事实,因此您不需要有一个&Result开始。相反,将Vec转换为迭代器并take块长度的片段:

fn foo(v: Vec<Result<SomeStruct>>) -> Result<()> {
let mut v = v.into_iter().peekable();
while v.peek().is_some() {
for value in v.by_ref().take(42) {
let _value = value?;
// do something with `value`
}
}
Ok(())
}

您还可以使用Itertools::chunks

use itertools::Itertools; // 0.8.1
fn foo(v: Vec<Result<SomeStruct>>) -> Result<()> {
for chunk in &v.into_iter().chunks(42) {
for value in chunk {
let value = value?;
// do something with `value`
}
}
Ok(())
}

另请参阅:

  • 为迭代器实现is_empty的规范方法是什么?
  • 是否有等效于切片::块/窗口,供迭代器循环对、三元组等?
  • 是否可以使用迭代器将向量分成 10 组?

了解错误

以下是Vec::chunks的签名:

pub fn chunks(&self, chunk_size: usize) -> Chunks<T>

请注意&self,因为返回的Chunks<T>具有Item = &'a [T]类型的迭代器。 当您迭代此切片(每个块都是一个切片)时,您将获得对T的引用。 因此,编译器给出的错误:

the trait `std::ops::Try` is not implemented for `&std::result::Result<SomeStruct, SomeError>`

基本上这是说Try没有为&Result<SomeStruct>实现。

溶液

我认为不可能拥有切片引用的所有权(不复制)。

复制到Vec

您可以在切片(块)上使用to_vec,前提是SomeStructSomeError实现Clone

使用SomeStruct作为参考:

由于您的代码返回Ok(()),因此您可能能够在//do smth with value中使用&SomeStruct。 事实上,这是 Rust 中一个常见且非常有用的模式:

fn do_something_with_value(v: &SomeStruct) {
unimplemented!();
}

最新更新