Borrowing error when calling `get` on HashMap

Hello, I'm trying to extract a filename out of a url, for example if receiving the path stuff/files?file=thing.txt, extracting thing.txt out of the path. I came across the crate url and noticed it had some helper methods for extracting a query from a url, namely query_pairs(). To get the value out of the (key, value) pairs I followed this stackoverflow answer, however I'm getting a borrowing error that I can't seem to get around. I know that the fields are in the HashMap as I can access them in a for loop and print them out, I just can't seem to be able to get the filename out into a variable and return it. I've tried using a reference of the HashMap instead, but ran into the same error. Any help would be appreciated!

My function:

fn parse_filename_from_url<'a>(url: &'a Url) -> &'a str {
    match url.query() {
        Some(query) => {
            let pairs_hash: HashMap<_, _> = url.query_pairs().into_owned().collect();
            match pairs_hash.get("file") {
                Some(file) => file,
                None => "tmp.bin",
            }
        },
        None => {
            "No query detected"
        }
    }
}

My error:

error[E0597]: `pairs_hash` does not live long enough
...
        match pairs_hash.get("file") {
              ^^^^^^^^^^ borrowed value does not live long enough
    },
    - borrowed value only lives until here [end of Some(query) block]
note: borrowed value must be valid for the lifetime 'a as defined on the function body ...
fn parse_filename_from_url<'a>(url: &'a Url) -> &'a str {
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

You really don't need a HashMap here - in fact, it's wasteful to build it up from the iterator, only to perform a single lookup. You can just find the "file" key in the iterator directly:

extern crate url;

use std::borrow::Cow;
use url::Url;

fn parse_filename_from_url<'a>(url: &'a Url) -> Option<Cow<'a, str>> {
    url.query_pairs()
        .find(|(key, _)| key == "file")
        .map(|(_, val)| val)
}

fn main() {
    let q = Url::parse_with_params(
        "https://example.com",
        &[("key", "val"), ("file", "some.bin")],
    ).unwrap();
    println!("{:?}", parse_filename_from_url(&q));
}

play

As for the issue with HashMap, it's built inside your function and contains Cow instances - these instances are owned by the HashMap. As such, you cannot return a &'a str from them to the caller because the HashMap will be destroyed inside this function, and the Cow keys and values with it.

1 Like

Awesome, that makes so much more sense, thank you!