当T无法复制时,我该如何将Option<T>列表转换为T列表?

21

如何处理一个 Vec<Option<T>>,其中 T 不能被复制,并且展开所有的 Some 值?

map 步骤中遇到了错误。我很乐意移动原始列表的所有权并“丢弃” None

#[derive(Debug)]
struct Uncopyable {
    val: u64,
}

fn main() {
    let num_opts: Vec<Option<Uncopyable>> = vec![
        Some(Uncopyable { val: 1 }),
        Some(Uncopyable { val: 2 }),
        None,
        Some(Uncopyable { val: 4 }),
    ];

    let nums: Vec<Uncopyable> = num_opts
        .iter()
        .filter(|x| x.is_some())
        .map(|&x| x.unwrap())
        .collect();
    println!("nums: {:?}", nums);
}

游乐场

这会引发错误

error[E0507]: cannot move out of borrowed content
  --> src/main.rs:17:15
   |
17 |         .map(|&x| x.unwrap())
   |               ^-
   |               ||
   |               |hint: to prevent move, use `ref x` or `ref mut x`
   |               cannot move out of borrowed content
2个回答

28
在Rust中,当您需要一个值时,通常需要移动元素或克隆它们。由于移动更为普遍,在这里进行了演示,只需要进行两个更改:
let nums: Vec<Uncopyable> = num_opts
    .into_iter()
//  ^~~~~~~~~~~~-------------- Consume vector, and iterate by value
    .filter(|x| x.is_some())
    .map(|x| x.unwrap())
//       ^~~------------------ Take by value
    .collect();

正如llogiq指出的那样,filter_map已经专门用于过滤掉None

let nums: Vec<Uncopyable> = num_opts
    .into_iter()
//  ^~~~~~~~~~~~-------- Consume vector, and iterate by value
    .filter_map(|x| x)
//              ^~~----- Take by value
    .collect();

然后它就能工作了(消耗 num_opts)。

正如@nirvana-msu所指出的,在Rust 1.33中,添加了std::convert::identity,可以代替|x| x。根据文档:

let filtered = iter.filter_map(identity).collect::<Vec<_>>();

这比我的解决方案更好(除非你想保留原始向量 - 你不能同时拥有两者)。请注意,您仍然可以使用 filter_map 来使其更短。 - llogiq
1
@llogiq:对我来说不清楚Uncopyable是否也意味着non-Clone;因此,也许OP会接受使用.cloned()的解决方案。感谢巧妙的filter_map - Matthieu M.
1
不客气。谢谢你使用 into_iter() - llogiq
1
你也可以使用identity代替|x| x。文档中甚至有一个覆盖这个确切用例的示例。 - nirvana-msu
flat_map是什么意思? - ecoe
@ecoe: 我不知道你在问什么...我鼓励你提出一个自己的问题,并提供详细的细节。 - Matthieu M.

4

如果您愿意使用指向原始Vec的引用的Vec,则根本不需要复制Uncopyable

let nums: Vec<&Uncopyable> = num_opts.iter().filter_map(|x| x.as_ref()).collect();
//            ^ notice the & before Uncopyable?

如果你需要使用一个要求 &[Uncopyable] 的API,那么这个方法可能对你无效。在这种情况下,可以使用 Matthieu M.的解决方案,该解决方案可以简化为:

let nums: Vec<Uncopyable> = num_opts.into_iter().filter_map(|x| x).collect();

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