连接字符串向量的向量

Concatenate a vector of vectors of strings

我正在尝试编写一个函数,该函数接收字符串向量的向量和 return 连接在一起的所有向量,即它 return 是一个字符串向量。

到目前为止我能做的最好的是:

fn concat_vecs(vecs: Vec<Vec<String>>) -> Vec<String> {
    let vals : Vec<&String> = vecs.iter().flat_map(|x| x.into_iter()).collect();
    vals.into_iter().map(|v: &String| v.to_owned()).collect()
}

但是,我对这个结果并不满意,因为看起来我应该能够从第一个 collect 调用中获得 Vec<String>,但不知何故我无法弄清楚如何去做。

我更想弄清楚为什么 collect 的return 类型是Vec<&String>。我试图从 API 文档和源代码中推断出这一点,但尽管我尽了最大努力,我什至无法理解函数的签名。

所以让我试着追踪每个表达式的类型:

- vecs.iter(): Iter<T=Vec<String>, Item=Vec<String>>
- vecs.iter().flat_map(): FlatMap<I=Iter<Vec<String>>, U=???, F=FnMut(Vec<String>) -> U, Item=U>
- vecs.iter().flat_map().collect(): (B=??? : FromIterator<U>)
- vals was declared as Vec<&String>, therefore 
      vals == vecs.iter().flat_map().collect(): (B=Vec<&String> : FromIterator<U>). Therefore U=&String.

我在上面假设类型推断器能够根据 vals 的类型计算出 U=&String。但是,如果我在代码中为表达式提供显式类型,则编译时不会出现错误:

fn concat_vecs(vecs: Vec<Vec<String>>) -> Vec<String> {
    let a: Iter<Vec<String>> = vecs.iter();
    let b: FlatMap<Iter<Vec<String>>, Iter<String>, _> = a.flat_map(|x| x.into_iter());
    let c = b.collect();
    print_type_of(&c);
    let vals : Vec<&String> = c;
    vals.into_iter().map(|v: &String| v.to_owned()).collect()
}

很明显,U=Iter<String>...请帮我清理一下这个烂摊子。

编辑: 感谢 bluss 的提示,我能够实现一个 collect 如下:

fn concat_vecs(vecs: Vec<Vec<String>>) -> Vec<String> {
    vecs.into_iter().flat_map(|x| x.into_iter()).collect()
}

我的理解是,通过使用 into_iter 我将 vecs 的所有权转移到 IntoIter 并进一步向下调用链,这使我能够避免在 lambda 调用中复制数据因此 - 神奇地 - 类型系统给了我 Vec<String> 以前总是给我 Vec<&String> 的地方。虽然看到高级概念如何反映在库的工作中当然非常酷,但我希望我知道这是如何实现的。

编辑 2: 经过艰苦的猜测过程,查看 API 文档并使用 this method 来破译类型,我得到了完整的注释(忽略生命周期):

fn concat_vecs(vecs: Vec<Vec<String>>) -> Vec<String> {
    let a: Iter<Vec<String>> = vecs.iter();
    let f : &Fn(&Vec<String>) -> Iter<String> = &|x: &Vec<String>| x.into_iter();
    let b: FlatMap<Iter<Vec<String>>, Iter<String>, &Fn(&Vec<String>) -> Iter<String>> = a.flat_map(f);
    let vals : Vec<&String> = b.collect();
    vals.into_iter().map(|v: &String| v.to_owned()).collect()
}

我会想:为什么在外层 vec 上使用 iter() 而在内层 vec 上使用 into_iter()?使用 into_iter() 实际上是至关重要的,这样我们就不必先复制内部向量,然后再复制内部的字符串,我们只需要获得它们的所有权即可。

我们实际上可以像求和一样写这个:将向量两两连接起来。由于我们总是重复使用同一个累加向量的分配和内容,所以这个操作是线性时间的。

为了尽量减少增长和重新分配矢量所花费的时间,请预先计算 space 所需的数量。

fn concat_vecs(vecs: Vec<Vec<String>>) -> Vec<String> {
    let size = vecs.iter().fold(0, |a, b| a + b.len());
    vecs.into_iter().fold(Vec::with_capacity(size), |mut acc, v| {
        acc.extend(v); acc
    })
}

如果你确实想克隆所有内容,已经有一个方法,你只需使用 vecs.concat() /* -> Vec<String> */


.flat_map 的方法很好,但是如果您不想再次克隆字符串,则必须在所有级别上使用 .into_iter():(xVec<String>).

vecs.into_iter().flat_map(|x| x.into_iter()).collect()

如果你想克隆每个字符串,你可以使用这个:(将 .into_iter() 更改为 .iter() 因为 x 这里是一个 &Vec<String> 并且这两种方法实际上都会产生同样的事情!)

vecs.iter().flat_map(|x| x.iter().map(Clone::clone)).collect()