How to modify this function to return iterator?

Hi,

I have this function that I use as a utility to open and parse input files:

pub fn process_input<F, T>(path: &str, f: F) -> Vec<T>
where
    F: Fn(&str) -> T,
{
    fs::read_to_string(path)
        .unwrap()
        .lines()
        .map(f)
        .collect::<Vec<T>>()
}

It works without a problem, but I occasionally have to convert the result back to iterator for further processing. I tried to modify it to return impl Iterator but I'm kind of stuck and not sure how to continue. I've reached several dead ends during my tries but here's the last one:

pub fn process_input<F, T>(path: &'static str, f: F) -> impl Iterator<Item=T>
where
    F: Fn(&'static str) -> T,
    T: std::clone::Clone
{
    fs::read_to_string(path)
        .unwrap()
        .lines()
        .map(f)
        .cloned()
}

This has the following errors:

    Checking advent-of-code-2019 v0.1.0 (/Users/haim/Documents/Code/Personal/advent-of-code-2019)
error[E0271]: type mismatch resolving `<std::iter::Map<std::str::Lines<'_>, F> as std::iter::Iterator>::Item == &_`
  --> src/inputs.rs:12:10
   |
3  | pub fn process_input<F, T>(path: &'static str, f: F) -> impl Iterator<Item=T>
   |                         - this type parameter
...
12 |         .cloned()
   |          ^^^^^^ expected type parameter `T`, found reference
   |
   = note:   expected type `T`
           found reference `&_`
   = help: type parameters must be constrained to match other types
   = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters

error[E0271]: type mismatch resolving `<F as std::ops::FnOnce<(&str,)>>::Output == &T`
 --> src/inputs.rs:3:57
  |
3 | pub fn process_input<F, T>(path: &'static str, f: F) -> impl Iterator<Item=T>
  |                         - this type parameter           ^^^^^^^^^^^^^^^^^^^^^ expected type parameter `T`, found `&T`
  |
  = note: expected type parameter `T`
                  found reference `&T`
  = help: type parameters must be constrained to match other types
  = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters
  = note: required because of the requirements on the impl of `std::iter::Iterator` for `std::iter::Map<std::str::Lines<'_>, F>`
  = note: required because of the requirements on the impl of `std::iter::Iterator` for `std::iter::Cloned<std::iter::Map<std::str::Lines<'_>, F>>`
  = note: the return type of a function must have a statically known size

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0271`.
error: could not compile `advent-of-code-2019`.

To learn more, run the command again with --verbose.

Not sure how to continue :frowning:, I'll appreciate any help helping me understand errors.

Thanks

So the issue here is that str::lines only borrows the string, but you want it to take ownership. In this case the easiest solution is probably just to use BufRead::lines instead.

use std::fs::File;
use std::io::{BufRead, BufReader};

pub fn process_input<F, T>(path: &str, f: F) -> impl Iterator<Item = T>
where
    F: Fn(&str) -> T,
{
    BufReader::new(File::open(path).unwrap())
        .lines()
        .map(move |s| f(s.unwrap().as_str()))
}

This doesn't read the entire file at once, though. I suppose you could also read it into memory and use a Cursor<Vec<u8>> instead of a file object.

Alternatively you would need to implement Iterator on your own.

1 Like

Great, Thanks :slight_smile:.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.