【发布时间】:2015-06-29 21:40:01
【问题描述】:
我正在尝试编写一个函数,它接收字符串向量的向量并返回连接在一起的所有向量,即它返回一个字符串向量。
到目前为止,我能做的最好的事情如下:
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 的返回类型是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()
}
【问题讨论】:
-
- vecs.iter(): Iter<T=Vec<String>, Item=Vec<String>>:这是不正确的。 structIter上没有名为Item的关联类型(只有特征可能具有关联类型)。vecs.iter()是Iter<Vec<String>>类型,但 this type implementsIterator<Item=&Vec<String>>(注意&)。当你flat_map这个时,你把&Vec<String>变成&String。
标签: vector concatenation rust