如何在结果为向量时获取结果的Ok值?

3
#[derive(Debug)]
struct S{}

#[derive(Debug)]
struct E{}

fn test() -> Result<S, E> {
    let data_1: Result<S, E> = Ok(S{});
    let data_2: Result<S, E> = Err(E{});
    let v: Vec<Result<S, E>> = vec![data_1, data_2];

    for i in 1..2 {
        for item in &v {
            let val = item?; //error
            println!("{:?}", val);
        };
    }

    Ok(S{})
}

在上面的代码中,如果结果为“Ok”(否则返回“Err”),我想打印该项的值。但由于将一个值移到共享引用之后,在(*item)?部分出现错误:
``` [rustc E0507] [E] cannot move out of `*item` which is behind a shared reference move occurs because `*item` has type `std::result::Result`, which does not implement the `Copy` trait ```
我尝试过克隆数据,但它并没有解决问题。此外,克隆并不正确。
有什么正确的解决方法/最佳实践吗?

1
for循环仍在内部使用迭代器。即遍历一个Item = &Result<S, E>的迭代器。如果我没记错,您可以通过使用方法(as_ref)将其转换为Result<&S, &E>。那么直接对每个项目进行模式匹配如何呢?if let Ok(val) = item { ... } - E net4
@trentcl 在这种情况下是可以的,但在我的实际情况中不行(原问题已更新)。我不能使用for item in v,因为该循环实际上嵌套在另一个循环内部。因此,这样做会导致“使用已移动的值:“v”。值在前一次循环迭代中移动到此处”错误。 - Pahlevi Fikri Auliya
1
如果在您的实际用例中,您有一个通过引用接收 v 的函数,因此无法按值迭代它,您可以使其返回 Result<S, &E>(其中 S 在函数内部创建,&E 是对参数的引用)。如果 v 在函数内部创建,但您想多次迭代它,则不知道是否有意义避免克隆 E - trent
1
也许你想要预处理 Vec<Result<S, E>> ,如果其中任何一个是 Err,则返回 Err,然后使用 Vec<S> 继续进行算法的其余部分?这听起来是合理的(尽管这引出了为什么首先有 ResultVec 的问题)。 - trent
显示剩余6条评论
1个回答

2

您可以格式化引用(例如&S)。但是?操作符需要在结果中返回错误,因此您必须将其复制、克隆或移动:

复制

如果您为自己的类型实现了Copy特性,则可以取消引用&Result<S, E> (playground):

#[derive(Debug, Copy, Clone)]
struct S {}

#[derive(Debug, Copy, Clone)]
struct E {}

如果错误类型实现了Copy,但是ok类型没有实现,则您也可以使其正常工作(playground)。

Clone

如果您需要将引用转换为不是Copy的拥有类型,则必须克隆类型。例如(playground):

#[derive(Debug, Clone)]
struct S {}

#[derive(Debug, Clone)]
struct E {}

let val = item.clone()?;

如果出现错误,您可以将此更改为仅克隆 (playground):

#[derive(Debug)]
struct S {}

#[derive(Debug, Clone)]
struct E {}

let val = item.as_ref().map_err(Clone::clone)?;

移动

如果你在迭代结束后不再需要这个向量,可以将它移动:

for item in v {  // this calls `IntoIterator::into_iter(v)` implicitly
    let val = item?;
    println!("{:?}", val);
}

或者,您可以先将 Vec<Result<S,E>> 转换为 Result<Vec <S>, E>

// collects the `Ok` values, or returns the first error it encounters
let v: Vec<S> = v.into_iter().collect::<Result<_, _>>()?;

for item in &v {
    println!("{:?}", item);
}

@pahlevi-fikri-auliya,感谢您接受我的答案!我可以问一下您使用了什么方法吗? - Aloso
它们都在我放在这里的简化代码中工作。但在我的真实代码中仍在尝试,例如答案中使用Clone需要在许多其他部分添加Clone。我对最后一个选项很好奇,为什么将Vec<Result<S, E>>转换为Result<Vec<S>, E>有帮助呢? - Pahlevi Fikri Auliya
最后一个例子是有效的,因为在带有 ?for 循环中没有返回错误。它只在 Result<Vec<S>, E> 上使用了 ?,而该类型不在引用之后。 - Aloso

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接