Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Borrow checker complains for closure inside loop if type not provided explicitly

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)?

like image 393
rsk Avatar asked Jan 31 '26 06:01

rsk


1 Answers

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.

like image 85
Anuradha Wickramarachi Avatar answered Feb 01 '26 22:02

Anuradha Wickramarachi



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!