Issue with borrow checker's evaluation order of function arguments

Hello friendly rustaceans,

I'm trying to learn rust and came upon an issue with function evaluation order. When I call something such as a.do(f(x)), I expect f(x) to be evaluated before .do(), but the borrow checker doesn't seem to agree. The following example shows such a case:

fn f(b: &Blob) -> f32 {
    b.a + 1.0
}
struct Blob {
    a: f32,
}
impl Blob {
    fn mutate_with(self, x: f32) -> Blob {
        Blob { a: self.a * x }
    }
}

fn main() {
    let blob = Blob { a: 0.0 };
    let blub = blob.mutate_with(f(&blob));
}

Explicitly specifying the evaluation order works, but quickly gets ugly when used in closures. I.e.

fn main() {
    let blob = Blob { a: 0.0 };
    let argh = f(&blob);
    let blub = blob.mutate_with(argh);
}

Is there a way to do without argh?

Another option I found would be

fn mutate_with(x: f32, blob: Blob) -> Blob {
    Blob { a: blob.a * x }
}

So to fix it, self would have to be the last instead of the first argument.

1 Like

When I call something such as a.do(f(x)) , I expect f(x) to be evaluated before .do() ,

It's not that .do is evaluated sooner; it's that a is. Consider this more complex case:

f1().foo(f2()).bar(f3())

I imagine you would agree that f1(), f2(), f3() should be called in that order. So, method call receivers must be evaluated before method call arguments. (The full ordering of calls is f1() f2() foo() f3() bar().)

Now, Rust does actually have a convenience to make this common pattern work for mutable borrows, called “two-phase borrows”. That is, this code works:

impl Blob {
    fn mutate_with(&mut self, x: f32) {
        self.a = self.a * x;
    }
}

fn main() {
    let mut blob = Blob { a: 0.0 };
    blob.mutate_with(f(&blob));
}

However, that works by creating the &mut blob that mutate_with needs in a special “reserved” state, which doesn't conflict with &blob because it isn't actually used until we're done with f(&blob). There's no analogue of this for moves. (Perhaps there should be, but the language doesn't have it.)

3 Likes