As the title says, I have an Iterator of Strings 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 Strings 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 Strings into an intermediate vector, so that I have somebody owning all the Strings, and am flatmapping an iterator of &Strings instead of Strings; but having to allocate that intermediate vector of Strings 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?