I have code that uses osmpbfreader
to iterate over objects in files. The problem is that I need to process it in 2-3 passes, and there's a lot of overhead in it to write the cycle every time.
// pass 1
let file = std::fs::File::open(&std::path::Path::new(osm_file_path))?;
let mut reader = OsmPbfReader::new(file)?;
for obj in reader.par_iter().map(Result::unwrap) {
match obj {
OsmObj::Way(way) => {
if !way.tags().contains_key("highway") { continue; }
// do useful work
}
_ => {}
}
}
// pass 2
let file = std::fs::File::open(&std::path::Path::new(osm_file_path))?;
let mut reader = OsmPbfReader::new(file)?;
for obj in reader.par_iter().map(Result::unwrap) {
match obj {
OsmObj::Way(way) => {
if !way.tags().contains_key("highway") { continue; }
// do another kind of work
}
_ => {}
}
}
// pass 3...
So I wanted to abstract this into a struct with Iterator trait. I checked Iterator trait, and it hasn't an init method to be called when for
loop starts, so I understand that I have to initialize the internal iterator myself and then get its items in next
.
Something like:
use osmpbfreader::reader::ParIter;
struct WaysIterator(ParIter);
impl WaysIterator {
fn new(osm_file_path) -> WaysIterator {
// create reader and do par_iter
}
}
impl Iterator for WaysIterator {
fn next() -> Option<Way> {
loop {
match self.0.next() {
Some(OsmObj::Way(way)) => {
if way.tags().contains_key("highway") {
return way
}
Some(x) => {}
None => return None
}
}
}
}
}
Unfortunately, I'm stuck at implementing new
.
The problem is that par_iter borrows the reader, not takes ownership, and upon returning the reader will be lost. So the compiler resists:
struct RoadIter<'a>(ParIter<'a, std::fs::File>);
impl RoadIter<'_> {
fn new(osm_file_path: &std::ffi::OsStr) -> Result<RoadIter, Box<dyn Error + 'static>> {
let file = std::fs::File::open(&std::path::Path::new(osm_file_path))?;
let mut reader = Box::new(OsmPbfReader::new(file));
let pi = reader.par_iter();
^^^^^^
error: src/main.rs:29: `*reader` is borrowed here
Ok(RoadIter(pi))
^^
error: src/main.rs:30: returns a value referencing data owned by the current function
error: src/main.rs:30: cannot return value referencing local data `*reader`
}
}
I tried to keep both the reader and ParIter in the struct, but the same error persists.
struct RoadIter<'a>(OsmPbfReader<std::fs::File>, ParIter<'a, std::fs::File>);
Box-ing it didn't help either.
struct RoadIter<'a>(Box<OsmPbfReader<std::fs::File>>, ParIter<'a, std::fs::File>);
...
let mut reader = Box::new(OsmPbfReader::new(file));
The same error: cannot move out reader, because it's borowed.
What can be done about this?