问题描述
正如标题所述,我有Iterator
个String
,并且想通过合并所有Vec<char>
(并同时考虑每个{{1} }(如String
一样)作为Unicode标量值的序列)。 (我不需要在.chars()
或类似的东西之间插入字符串,尽管我知道使用String
并不会困难得多,例如other answers )
itertools::intersperse
(这很好,但不是特别必要,如果我可以实现稍微更通用的类型签名:)
pub fn flatten_strings(ss: impl Iterator<Item=String>) -> Vec<char> {
// ???
}
无论如何,这是一个简单的尝试:
pub fn flatten_strings<S>(ss: impl Iterator<Item=String>) -> S where S: FromIterator<char> {
// ???
}
不幸的是,这不起作用,因为pub fn flatten_strings(ss: impl Iterator<Item=String>) -> Vec<char> {
ss.flat_map(|s| s.chars()).collect()
}
返回Chars
,其中包含对它所基于的chars
的引用;但是lambda拥有String
的所有权,并在返回时将其删除。
String
我可以通过将所有error[E0515]: cannot return value referencing function parameter `s`
--> src/main.rs:2:21
|
2 | ss.flat_map(|s| s.chars()).collect()
| -^^^^^^^^
| |
| returns a value referencing data owned by the current function
| `s` is borrowed here
收集到一个中间向量中来解决此问题,以便让某人拥有所有String
,并映射String
的迭代器代替&String
s;但是必须分配String
s的中间向量似乎效率低下,并且在一定程度上无法使用Rust的漂亮迭代器抽象:
String
类似地,我可以滚动一个循环,但这看起来也很丑。是否可以在没有其他分配的情况下有效地实现此功能,而且还可以保留Rust的迭代器抽象?
解决方法
主要问题是在任何人都可以使用依赖于它的s: String
对象之前,先删除闭包中的Chars
参数。
有两种解决方法。第一个比代码更冗长,但是使用相同的类型签名。第二个依赖于&str
的迭代器,该迭代器使函数及其所有Chars
对象失效。
pub fn flatten_strings(ss: impl Iterator<Item=String>) -> Vec<char> {
let mut res = Vec::new();
for s in ss {
res.extend(s.chars());
}
res
}
pub fn flatten_strings2<'a>(ss: impl Iterator<Item=&'a str>) -> Vec<char> {
ss.flat_map(|s| s.chars()).collect()
}
,
如果您可以使用临时分配,则可以在浏览列表时将每个字符串转换为Vec<char>
并将其提供给flat_map。这将使更一般的情况也起作用。
您可以稍微概括一些(如Sven Marnach的建议),只返回一个impl Iterator<Item = char>
而无需收集,并允许呼叫者收集它们。
use std::collections::HashSet;
use core::iter::FromIterator;
pub fn flatten_strings(ss: impl Iterator<Item=String>) -> Vec<char> {
ss.flat_map(|s| s.chars().collect::<Vec<_>>()).collect()
}
pub fn flatten_strings2<S>(ss: impl Iterator<Item=String>) -> S where S: FromIterator<char> {
ss.flat_map(|s| s.chars().collect::<Vec<_>>()).collect()
}
pub fn flatten_strings3(ss: impl Iterator<Item=String>) -> impl Iterator<Item = char> {
ss.flat_map(|s| s.chars().collect::<Vec<_>>())
}
fn main() {
let v = vec!["A string ".to_string(),"another".to_string() ];
println!("{:?}",flatten_strings(v.clone().into_iter()));
let h: HashSet<char> = flatten_strings2(v.clone().into_iter());
println!("{:?}",h);
let h: HashSet<char> = flatten_strings3(v.clone().into_iter()).collect();
println!("{:?}",h);
}