Timofey Gorshkov
Timofey Gorshkov

Reputation: 5125

Function to read and parse string into Iterator in Rust

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

Answers (2)

Matthieu M.
Matthieu M.

Reputation: 299800

Iterators are lazy, therefore they must borrow their input:

  • this requires that their input lives longer than they do
  • this requires, for safety reason, that the borrowing relationship be exposed at function boundaries

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

A.B.
A.B.

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

Related Questions