As the title says, I have an Iterator
of String
s and would like to produce a Vec<char>
, just by concatenating all of them (and while considering each String
as a sequence of Unicode Scalar Values, as .chars()
does). (I don't need to interleave a string between the String
s or anything like that, although I understand it wouldn't be much harder with, e.g. itertools::intersperse
, as covered in other answers.)
pub fn flatten_strings(ss: impl Iterator<Item=String>) -> Vec<char> {
// ???
}
(It would be nice, but not particularly necessary, if I could implement the slightly more general type signature:)
pub fn flatten_strings<S>(ss: impl Iterator<Item=String>) -> S where S: FromIterator<char> {
// ???
}
Anyway, here's a simple attempt:
pub fn flatten_strings(ss: impl Iterator<Item=String>) -> Vec<char> {
ss.flat_map(|s| s.chars()).collect()
}
Unfortunately, this doesn't work, because chars
returns Chars
, which contains a reference to the String
it's based on; but the lambda took ownership of the String
and will drop it when it returns.
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
I can fix this by just collecting all the String
s into an intermediate vector, so that I have somebody owning all the String
s, and am flatmapping an iterator of &String
s instead of String
s; but having to allocate that intermediate vector of String
s seems inefficient and somewhat defeat the point of using Rust's nice iterator abstractions:
pub fn flatten_strings(ss: impl Iterator<Item=String>) -> Vec<char> {
ss.collect::<Vec<String>>().iter().flat_map(|s| s.chars()).collect()
}
Similarly, I could handroll a loop, but that also seems ugly. Can I implement this function efficiently without additional allocations, but also without leaving Rust's iterator abstractions?