Reputation: 5125
Is there a way to write a function that will look like this:
fn read_and_iter_u32_line<'a>(mut buf: String) -> Iterator<Item=u32> {
buf.truncate(0);
io::stdin().read_line(&mut buf).unwrap();
buf.split_whitespace()
.map(|s| s.parse::<u32>().unwrap())
}
Upvotes: 2
Views: 1730
Reputation: 299800
Iterators are lazy, therefore they must borrow their input:
The former point requires that the buf
be passed as a reference and not a value.
The latter point prevents you from returning a generic Iterator<Item=u32>
(even boxed) because it hides the borrowing relationship between the returned iterator and the passed in buffer. So instead you must be explicit:
use std::io;
fn read_and_iter_u32_line<'a>(buf: &'a mut String)
-> std::iter::Map<std::str::SplitWhitespace<'a>, fn(&'a str) -> u32>
{
fn convert(s: &str) -> u32 { s.parse().unwrap() }
buf.truncate(0);
io::stdin().read_line(buf).unwrap();
buf.split_whitespace().map(convert)
}
fn main() {
let mut buf = "".to_string();
for i in read_and_iter_u32_line(&mut buf) {
println!("{}", i);
}
}
Note: actually, the lifetime annotation can be elided, but I have exposed it here to highlight why the generic iterator is not possible.
Upvotes: 3
Reputation: 16630
Iterators are lazy. This means the data they are operating on needs to exist as long as the iterator itself, but buf
ceases to exist when the function returns. If we keep buf
around for longer it can work though.
Writing functions that return complex iterators is tricky at the moment, but it's possible:
use std::io;
use std::iter::{Iterator, Map};
use std::str::SplitWhitespace;
fn read_and_iter_u32_line(buf: &mut String) -> Map<SplitWhitespace, fn(&str) -> u32> {
buf.truncate(0);
io::stdin().read_line(buf).unwrap();
buf.split_whitespace().map(parse)
}
fn parse(s: &str) -> u32 {
s.parse::<u32>().unwrap()
}
Upvotes: 5