连接字符串向量的向量

kir*_*lkh 4 vector concatenation rust

我正在尝试编写一个函数来接收字符串向量的向量,并返回连接在一起的所有向量,即它返回一个字符串向量.

到目前为止我能做的最好的事情如下:

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()
}
Run Code Online (Sandbox Code Playgroud)

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

我更感兴趣的是弄清楚为什么返回类型collect是正确的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.
Run Code Online (Sandbox Code Playgroud)

我假设上面的类型推断器能够U=&String根据类型来确定vals.但是如果我在代码中给表达式赋予显式类型,则编译时没有错误:

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()
}
Run Code Online (Sandbox Code Playgroud)

显然,U=Iter<String>......请帮我澄清这个烂摊子.

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

fn concat_vecs(vecs: Vec<Vec<String>>) -> Vec<String> {
    vecs.into_iter().flat_map(|x| x.into_iter()).collect()
}
Run Code Online (Sandbox Code Playgroud)

我的理解是,通过使用into_iter我将所有权转移vecsIntoIter调用链和调用链的下游,这允许我避免在lambda调用中复制数据,因此 - 神奇地 - 类型系统Vec<String>给我Vec<&String>以前总是给我的地方.虽然看到高级概念如何反映在图书馆的运作中当然非常酷,但我希望我知道如何实现这一目标.

编辑2:经过艰苦的猜测过程,查看API文档并使用此方法解密类型,我完全注释了它们(忽略生命周期):

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()
}
Run Code Online (Sandbox Code Playgroud)

blu*_*uss 6

我想一想:你为什么在外部vec上使用iter()而在内部vec上使用into_iter()?使用into_iter()实际上是至关重要的,因此我们不必首先复制内部向量,然后是内部的字符串,我们只是获得它们的所有权.

我们实际上可以像求和一样编写它:将两个向量连接起来.由于我们总是重用相同累加向量的分配和内容,因此该操作是线性时间.

为了最大限度地减少生长和重新分配矢量所花费的时间,请预先计算所需的空间.

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
    })
}
Run Code Online (Sandbox Code Playgroud)

如果你想要克隆所有内容,那么已经有了一种方法,你只需要使用它 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()