我该如何迭代一个Option<Vec<_>>?

10
我正在尝试迭代Option<Vec<>>
#[derive(Debug)]
pub struct Person {
    pub name: Option<String>,
    pub age: Option<u64>,
}

#[derive(Debug)]
pub struct Foo {
    pub people: Option<Vec<Person>>,
}

我一开始天真地使用了

for i in foo.people.iter() {
    println!("{:?}", i);
}

我并不是遍历整个Vec的所有元素,而是显示整个Vec。这就像我在遍历Option的唯一引用。

使用以下代码,我正在遍历Vec的内容:

for i in foo.people.iter() {
    for j in i.iter() {
        println!("{:?}", j);
    }
}

我不确定这是最愉快的语法,我认为你应该先取消Option,以便实际迭代集合。

然后我不知道你在哪里可以使用Option::iter,如果你总是有一个单一的引用。

这里是游乐场的链接。


1
什么问题? - Lee
我不明白在什么情况下应该使用Option<>.iter(),我可能错误地感觉它总是返回单个项目。 - Xavier T.
@XavierT。Option::iter旨在处理您希望将选项视为零或一个元素容器的情况。我也觉得它似乎没有什么用,但这就是设计初衷。 - user4815162342
@user4815162342 let foo: Vec<Option<T>> = vec![]; let bar: Vec<T> = foo.into_iter().flat_map(|x| x).collect();. - Shepmaster
4个回答

23

如另一个回答的评论中所提到的,我会使用以下内容:

// Either one works
foo.people.iter().flatten()
foo.people.iter().flat_map(identity)

Option<T>iter 方法将返回一个元素为一或零的迭代器。

flatten 会获取每个元素(在这种情况下是 &Vec<Person>),并将其嵌套的元素展平。

这与使用 flat_mapidentity 相同,后者获取每个元素(在此情况下为 &Vec<Person>)并展平其嵌套的元素。

两条路径都会得到一个 Iterator<Item = &Person>


请注意,如果 foo.people 是一个堆叠借用的 Option(即 foo.people: Option<&&Vec<i32>>),则应使用 .cloned() 取消堆叠 引用。例如:foo.people.cloned().iter().flatten() - Ben
@Ben 你知道在"堆叠借用"的情况下还有其他方法吗?我想遍历Option<&HashMap<...,...>>并将None视为空HashMap,而无需复制内容。 - Splines

11

Option有一个iter方法,它“遍历可能包含的值”,即提供Option中的单个值(如果选项是Some),或者根本没有值(如果选项是None)。因此,如果要将选项视为容器,其中None表示容器为空,而Some表示它包含单个元素,则很有用。

要遍历基础元素的值,您需要从foo.people.iter()切换到foo.people.unwrap().iter()foo.people.unwrap_or_else(Vec::new).iter(),具体取决于您是否希望程序在遇到None时发生崩溃或不进行迭代。

可编译的示例在playground中。


3
可以将迭代过程中的 unwrap 替换为 if let Some(ref v) = foo.people {},这样更简洁易懂。 - oli_obk
7
如果我想要遍历这些人,我可能会使用foo.people.iter().flat_map(|v|v.iter()),当为None时,它将返回一个空的迭代器,否则将返回向量中的所有元素。 - Lukazoid
5
@Lukazoid 不需要用 v.iter(),只需要使用 v 即可。 - Shepmaster
@Shepmaster,我没有注意到flat_map处理IntoIterator,这样更好。 - Lukazoid
@Boiethios 这并不比 unwrap 更好(从风格上来说),因为它有不同的行为。我认为它也不比 unwrap_or_else 更好,因为 unwrap_or_else 可以更清晰地表达正在发生的事情,而不依赖于 Option 的容器特性。(此外,Vec::new() 保证不会分配内存,所以在 None 情况下不会损失性能。)根据生成的汇编代码(playground),unwrap_or_else 版本也更有效率。 - user4815162342
显示剩余4条评论

6

1
如果您不需要IntoIterator实现的实际值,可以使用显式的if let代替:
if let Some(x) = foo.people {
    for i in x {
        // work with i here
    }
}

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