How do I return a boxed iterator that returns boxed iterators of f64?

Viewed 141

I'm trying to read a CSV file using the CSV crate and lazily cast all its cells to f64:

use csv::ReaderBuilder; // 1.1.4

fn get_df_iterator(path: &str) {
    // Build the CSV reader and iterate over each record casting to f64
    let rdr = ReaderBuilder::new()
        .delimiter(b'\t')
        .from_path(path)
        .unwrap();

    rdr.into_records().map(|record_result| {
        record_result
            .unwrap()
            .into_iter()
            .map(|cell: &str| cell.parse::<f64>().unwrap())
    })
}

fn main() {
    let m1 = get_df_iterator("df1.csv");
}

playground

I've read that to return types with closures in stable Rust we can use Boxes. I've tried with:

use csv::{Error, ReaderBuilder, StringRecord};

fn get_df_iterator_box(path: &str) -> Box<dyn Iterator<Item = Box<dyn Iterator<Item = f64>>>> {
    // Build the CSV reader and iterate over each record.
    let rdr = ReaderBuilder::new()
        .delimiter(b'\t')
        .from_path(path)
        .unwrap();

    // Returns Box<Iterator<Item = f64>>
    let parse_str = |cell: &str| cell.parse::<f64>().unwrap();

    let parse_record = |record_result: Result<StringRecord, Error>| {
        Box::new(record_result.unwrap().into_iter().map(parse_str))
    };

    // Box<Iterator<Item = Box<Iterator<Item = f64>>>>
    Box::new(rdr.into_records().map(parse_record))
}

fn main() {
    let m1 = get_df_iterator_box("df1.csv");
}
error[E0271]: type mismatch resolving `<[closure@src/main.rs:13:24: 15:6] as FnOnce<(std::result::Result<StringRecord, csv::Error>,)>>::Output == Box<(dyn Iterator<Item = f64> + 'static)>`
  --> src/main.rs:18:5
   |
11 |     let parse_str = |cell: &str| cell.parse::<f64>().unwrap();
   |                     ----------------------------------------- the expected closure
...
18 |     Box::new(rdr.into_records().map(parse_record))
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `Map`, found trait object `dyn Iterator`
   |
   = note: expected struct `Box<Map<StringRecordIter<'_>, [closure@src/main.rs:11:21: 11:62]>>`
              found struct `Box<(dyn Iterator<Item = f64> + 'static)>`
   = note: required because of the requirements on the impl of `Iterator` for `Map<StringRecordsIntoIter<File>, [closure@src/main.rs:13:24: 15:6]>`
   = note: required for the cast to the object type `dyn Iterator<Item = Box<(dyn Iterator<Item = f64> + 'static)>>`

I don't understand what the problem is. It's supposed to return a Box which contains an iterator that returns Boxes of iterators of f64. Here there's another playground with that update! If you want to replicate with the same file I'm using here you can download it.

1 Answers

You can specify the return type of the closure to tell Rust that you are returning a dyn trait:

let parse_record =
    |record_result: Result<StringRecord, Error>| -> Box<dyn Iterator<Item = f64>> {
        Box::new(record_result.unwrap().into_iter().map(parse_str))
    };

An alternative method would be to explicitly cast within the closure:

let parse_record = |record_result: Result<StringRecord, Error>| {
    Box::new(record_result.unwrap().into_iter().map(parse_str)) as Box<dyn Iterator<Item = f64>>
};

You could instead return impl trait instead of using dynamic dispatch and avoid this altogether:

fn get_iterator(path: &str) -> impl Iterator<Item = impl Iterator<Item = f64>> { ... }

For more information about returning traits, see What is the correct way to return an Iterator?

Related