我正在尝试编写一个函数,该函数接收字符串向量的向量并返回连接在一起的所有向量,即它返回字符串向量。
到目前为止我能做的最好的事情如下:
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()
}
然而,我对这个结果并不满意,因为看起来我应该能够得到Vec<String>
从一开始collect
打电话,但不知何故我不知道该怎么做。
我更有兴趣弄清楚why正是返回类型collect
is 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.
我假设上面的类型推断器能够弄清楚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()
}
清楚地,U=Iter<String>
...请帮我清理这个烂摊子。
EDIT:感谢 bluss 的提示,我能够实现一个collect
如下:
fn concat_vecs(vecs: Vec<Vec<String>>) -> Vec<String> {
vecs.into_iter().flat_map(|x| x.into_iter()).collect()
}
我的理解是通过使用into_iter
我转让所有权vecs
to IntoIter
并进一步沿着调用链,这使我能够避免复制 lambda 调用内的数据,因此 - 神奇地 - 类型系统给了我Vec<String>
它曾经总是给我Vec<&String>
前。虽然看到高级概念如何反映在图书馆的运作中确实非常酷,但我希望我知道这是如何实现的。
EDIT 2:经过费力的猜测过程,查看 API 文档并使用这个方法 https://stackoverflow.com/a/29168659/3646645为了破译这些类型,我对它们进行了完整注释(忽略生命周期):
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()
}