Return lazy iterator that depends on data allocated within the function

☆樱花仙子☆ 提交于 2019-12-05 05:00:15
Shepmaster

The straightest path to convert the original function would be to simply wrap the iterator. However, doing so directly will lead to problems because you cannot return an object that refers to itself and the result of decode refers to the Reader. If you could surmount that, you cannot have an iterator return references to itself.

One solution is to simply re-create the DecodedRecords iterator for each call to your new iterator:

fn search_iter<'a, P>(data_path: P, city: &'a str) -> MyIter<'a>
    where P: AsRef<Path>
{
    let file = File::open(data_path).expect("Opening file failed!");

    MyIter {
        reader: csv::Reader::from_reader(file).has_headers(true),
        city: city,
    }
}

struct MyIter<'a> {
    reader: csv::Reader<File>,
    city: &'a str,
}

impl<'a> Iterator for MyIter<'a> {
    type Item = DataRow;

    fn next(&mut self) -> Option<Self::Item> {
        let city = self.city;

        self.reader.decode()
            .map(|row| row.expect("Failed decoding row"))
            .filter(|row: &DataRow| row.city == city)
            .next()
    }
}

This could have overhead associated with it, depending on the implementation of decode. Additionally, this might "rewind" back to the beginning of the input — if you substituted a Vec instead of a csv::Reader, you would see this. However, it happens to work in this case.

Beyond that, I'd normally open the file and create the csv::Reader outside of the function and pass in the DecodedRecords iterator and transform it, returning a newtype / box / type alias around the underlying iterator. I prefer this because the structure of your code mirrors the lifetimes of the objects.

I'm a little surprised that there isn't an implementation of IntoIterator for csv::Reader, which would also solve the problem because there would not be any references.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!