b-r-u / osmpbf

A Rust library for reading the OpenStreetMap PBF file format (*.osm.pbf).
Apache License 2.0
122 stars 19 forks source link

cannot be stored outside of its closure while extracting useful data #1

Closed jens1o closed 5 years ago

jens1o commented 5 years ago

minimal working test case:

extern crate osmpbf;

use std::collections::HashMap;
use std::error::Error;
use std::time::Instant;

fn main() -> Result<(), Box<dyn Error>> {
    let reader = osmpbf::ElementReader::from_path("your-file.osm.pbf")?;
    let start_instant = Instant::now();

    let mut interesting_ways: HashMap<i64, Vec<(_, _)>> = HashMap::new();

    reader
        .for_each(|mut element| {
            if let osmpbf::Element::Way(way) = element {
                if way
                    .tags()
                    .find(|(k, _v)| k == &"wikipedia" || k == &"wikidata")
                    .is_some()
                {
                    for (key, value) in way.tags() {
                        interesting_ways
                            .entry(way.id())
                            .or_default()
                            .push((&key, &value));
                    }
                }
            }
        })
        .unwrap();

    println!(
        "Processing took {:?} (interesting ways: {})",
        start_instant.elapsed(),
        interesting_ways.len()
    );

    Ok(())
}

error:

error: borrowed data cannot be stored outside of its closure
  --> src\main.rs:17:48
   |
12 |     let mut interesting_ways: HashMap<i64, Vec<(_, _)>> = HashMap::new();
   |         -------------------- borrowed data cannot be stored into here...
...
16 |         .for_each(|element| {
   |                   --------- ...because it cannot outlive this closure
17 |             if let osmpbf::Element::Way(way) = element {
   |                                                ^^^^^^^ cannot be stored outside of its closure

error: aborting due to previous error

Could you help me please how I could achieve this?

Thank you for your work, seems pretty useful. :)

b-r-u commented 5 years ago

This should work:

extern crate osmpbf;

use std::collections::HashMap;
use std::error::Error;
use std::time::Instant;

fn main() -> Result<(), Box<dyn Error>> {
    let reader = osmpbf::ElementReader::from_path("your-file.osm.pbf")?;
    let start_instant = Instant::now();

    let mut interesting_ways: HashMap<i64, Vec<(_, _)>> = HashMap::new();

    reader
        .for_each(|element| {
            if let osmpbf::Element::Way(way) = element {
                if way
                    .tags()
                    .find(|(k, _v)| k == &"wikipedia" || k == &"wikidata")
                    .is_some()
                {
                    for (key, value) in way.tags() {
                        interesting_ways
                            .entry(way.id())
                            .or_default()
                            .push((key.to_string(), value.to_string()));
                    }
                }
            }
        })
        .unwrap();

    println!(
        "Processing took {:?} (interesting ways: {})",
        start_instant.elapsed(),
        interesting_ways.len()
    );

    Ok(())
}

Note the to_string() calls. Keys and values are only valid inside the parsing context as they are references into a string table that is shared by many elements. Creating an owned String with to_string fixes this issue.

(Also: first issue \o/)

jens1o commented 5 years ago

Ah, awesome, works fine! Thank you very much for your help. :)