Hello!
I'm trying to get an intuitive understanding of the difference(s) between the two scenarios shown in examples below, resulting in a compile error (borrow checker error) in the first one. Note that I'm not really looking for ways to make the first example compile (e.g using move
or calling .collect
on the inner iterator), but rather looking to really understand why it doesn't compile.
In the non-compiling example the compiler seems to treat name: &String
as data owned by the outer closure, which I guess makes sense if we're referring to the reference value itself. But the second example also borrows name
, with the only differences being the call to .as_str
and collecting into a Vec<(&str, &i32)>
. The one reasonable idea I've managed to come up with is that the compiler somehow is able to use the call to .as_str
to infer the lifetime as a borrow of the arg
function input parameter rather than the name
closure parameter.
fn borrow_error(arg: HashMap<String, Vec<i32>>) {
let all_tasks: Vec<(&String, &i32)> = arg
.iter()
.flat_map(|(name, values)| {
values.iter().map(|x| (
// ^^^ may outlive borrowed value `name`
name,
// ---- `name` is borrowed here
x,
))
})
.collect();
}
fn works(arg: HashMap<String, Vec<i32>>) {
let all_tasks: Vec<(&str, &i32)> = arg
.iter()
.flat_map(|(name, values)| {
values.iter().map(|x| (
name.as_str(),
x,
))
})
.collect();
}
I tried to implement the closures by hand in the manner usually described by people explaining how closures are implemented: Playground Link, but this attempt failed to reproduce the borrowing error.
Any help appreciated! Thanks.