NLL and let bindings

Hi, I'm wondering why even with NLL, let bindings are not transparent to the borrow checker. In particular, in the following snippet:

struct Foo {
    i: usize,
}

impl Foo {
    fn f1(&mut self) -> usize {
        self.i += 1;
        self.i
    }
    
    fn f2(&mut self, j: usize) {
        self.i += j;
    }
}

pub fn main() {
    let mut foo = Foo{i: 0};
    
    // fails
    foo.f2(foo.f1());
    
    // works
    {
        let x = foo.f1();
        foo.f2(x);
    }
}

it seems to me like the two blocks of code should be equivalent; or at least, that the first one could be turned into the second one automatically. This makes chaining functions somewhat harder. Is there a deep reason why the two are not equivalent? (I suspect something related to Drop but would love to hear the actual reason).

The reason is that values never go out of scope in the middle of an expression.

The dot method syntax is just a synthetic sugar over the UFCS(Universal Function Call Syntax). Without sugar the code would looks like this.

Foo::f2(&mut foo, Foo::f1(&mut foo));

Function parameters are evaluated in left to right order, so the f1 cannot be evaluated as the first argument of the f2 is still alive.

4 Likes

Ah yeah, that's a better way to talk about it.

That's helpful indeed, thank you. If order of evaluation was right-to-left, this would work, then? It seems like &mut self should really be evaluated last, since it comes up as soon as you have a context + reference in it or something like that. :confused:

There is a special case for method calls that allows &mut self to be "borrowed last" in some cases. This is called Two-Phase Borrows. But currently, it works only if the "outer" call (f2 in your example) takes &mut self while the "inner" call (f1) takes &self.

This rule could be modified to allow code like yours, where both calls take &mut self. This was discussed as a possible extension in the RFC, but it was rejected for now because it would have added complexity, in both design and implementation. However, the RFC notes, "it would be possible to loosen the rules in the future."

2 Likes

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.