&String as AsRef<Path>

So I am creating queue of (PathBuf, String) tuples

let mut config_value = config_content.parse::<Value>().unwrap();
let config: &mut _ = config_value.as_table_mut().unwrap();
let root = match config.remove("root") {
    Some(r) => config_path.join(r.as_str().unwrap()),
    None => config_path
};
let queue: VecDeque<(PathBuf, String)> = VecDeque::from_iter(
    config.into_iter().map(
        |(k, v)| v.as_table().unwrap().into_iter().map(
            |(n, u)| (root.join(k).join(n), u.as_str().unwrap().to_string()))).flatten());

The unfortunate thing is that I got my lifetimes and types messed up

error[E0597]: `k` does not live long enough
  --> src\main.rs:46:37
   |
46 |                 |(n, u)| (root.join(k).join(n), u.as_str().unwrap().to_string()))).flatten());
   |                 --------            ^ borrowed value does not live long enough  - `k` dropped here while still borrowed
   |                 |
   |                 value captured here

The k is &String
The join method requires AsRef<Path>

Not only I don’t understand how to fix it (i tried cloning, to_owned, explicitly borrowing), I also don’t understand why it doesn’t work “as is”.

This is a case of a closure desugaring to the “wrong” thing, or rather, not to the thing you expect. Specifically, the closure inside the inner map() call ends up capturing &&k (a &&String) - this borrow of a borrow does not live long enough because the “outer” borrow is confined to the scope of the first map() call, but the borrow needs to last beyond that, until the flatten() call, when the iterator chain is actually traversed.

The fix would be to use move closures so that you capture &k, while also being careful not to move root so that it can be reused across the iterator calls:

let queue: VecDeque<(PathBuf, String)> = VecDeque::from_iter(
        config
            .into_iter()
            .map(|(k, v)| {
                // capture a reference to `root` so that only the reference is moved below
                let root = &root;
                v.as_table()
                    .unwrap()
                    .into_iter()
                    .map(move |(n, u)| (root.join(k).join(n), u.as_str().unwrap().to_string()))
            })
            .flatten(),
    );
1 Like

I still don’t understand all the details here (especially the meaning of move keyword for lambdas), but I will figure it out on my own.

That works, thank you very much.