I have this code, for which borrow checker shows error:
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=55d050b6f25410ce0e17ef9e844b048d
fn f1(v: &str) {
}
fn main() {
let c = |v| f1(v);
for _ in 0..1 {
let s = String::new();
c(&s);
}
}
|
10 | c(&s);
| - ^^ borrowed value does not live long enough
| |
| borrow later used here
11 | }
| - `s` dropped here while still borrowed
But if I add explicit type to closure, code compiles let c = |v: &str| f1(v);
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=54ca20dff5bdf1831ec705481e4936bb
Can someone explain why it works in second case and not in first, as I understand rust correctly infers same type in first example (as it works if I run closure outside of loop)?
This is because the result of closure c outlives the loop. For example, in the loop, you pass &s to c but at the end of the loop c still exist. This is why the in-place construction of closure works. Even the following code works alright.
fn f1(v: &str) {}
fn main() {
for _ in 0..1 {
let s = String::new();
let c = |v| f1(v);
c(&s);
}
}
In essence, the borrowed reference cannot be held by someone when you know the reference cease to exist before the borrower.
Also note the following approaches work too;
fn f1(v: &str) {}
fn main() {
let c = f1;
for _ in 0..1 {
let s = String::new();
c(&s);
}
}
and
fn f1(v: &str) {}
fn c(v: &str) {}
fn main() {
for _ in 0..1 {
let s = String::new();
c(&s);
}
}
Because in both of these cases, your borrowed reference does not outlive the scopes. But in your closure syntax v the input goes to f(v) which is held by c.
In a way you can think that c is a variable that would hold onto the reference which dies before c does.
Your example is interesting and might even be an edge case. In case you were wondering closures capture the surrounding unlike variables and functions. This is discussed here further rust book.
Because the following code compiles.
fn main() {
let mut c;
for _ in 0..1 {
let s = String::new();
c = &s ;
}
}
But this fails
fn main() {
let mut c;
for _ in 0..1 {
let s = String::new();
c = &s ;
}
println!("{c}");
}
Because compiler knows in previous scenario c can be cleared right at the end of for loop, but the second one is a clear no, due to outlived reference.
I hope this shed some light on your question.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With