Why can I not call FnMut twice in a line?
Asked Answered
C

1

8

Taking example snippets from here: the following doesn't compile

fn foobar<F>(mut f: F)
    where F: FnMut(i32) -> i32
{
    println!("{}", f(f(2))); 
    // error: cannot borrow `f` as mutable more than once at a time
}

fn main() {
    foobar(|x| x * 2);
}

but this does

fn foobar<F>(mut f: F)
    where F: FnMut(i32) -> i32
{
    let tmp = f(2);
    println!("{}", f(tmp)); 
}
 
fn main() {
    foobar(|x| x * 2);
}

I don't understand why the first snippet is illegal: it's effectively the same as the second one, just written more concisely. More specifically, why must f(f(2)) mutably borrow f twice? It can simply borrow the inner f to compute the value of f(2), and then borrow the outer f and apply it to the value.

Cluck answered 26/9, 2021 at 20:56 Comment(2)
Pretty sure this is just a case the borrow-checker doesn't cover properly, its kinda similar to this Q&ALyrist
yeah I agree this is not intuitive as it seems the same but the borrow checker here isn't going deep enough to realize what's going on. I do think imho that the outcome is better. f(f(2)) isn't very nice and adding one of the values into a variable results in better readability. But again just a personal opinion.Dodecasyllable
S
17

More specifically, why must f(f(2)) mutably borrow f twice?

The borrows here happen in the order of expression evaluation, and expression evaluation is always left-to-right, even when the expressions in question are trivial variable accesses. The expressions to be evaluated in this code are:

  • f(f(2)) is made up of two subexpressions: f and f(2).
    1. Evaluate the function value, f (and borrow it as &mut because we're calling a FnMut).
    2. Evaluate the argument, f(2).
      1. Evaluate the function value, f; error because it's already borrowed.
      2. Evaluate the argument, 2.
      3. Call the borrow of f with the argument, 2. This is the result of f(2).
    3. Call the borrow of f with the argument, the result of evaluating f(2). This is the result of f(f(2))

The borrow checker could soundly accept this case, but it would require the idea of recognizing that the first borrow hasn't been used yet, which isn't currently a thing in the borrow checker.

Stroke answered 26/9, 2021 at 22:45 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.