Rust expression order of evaluation


#21

IIRC, Stability RFC proposed keeping packages on crates.io in desugared UFCS form with all type annotations. In this case, it would highly desirable for evaluation order to be the same for sugared operator form and desugared UFCS form (“method calls only”).


#22

I did say the ‘this’ parameter would always be evaluated last - that would still hold for UFCS.


#23

I’m not how that would still hold for UFCS? Are you envisioning that if you call Foo::foo and it is declared with &self that behaves differently than if it were declared self: &Foo? That is not really possible, because it’d have to be encoded in the fn type and so forth. I’m not really a fan of deviating from LTR in the case of method calls. We can make the borrow checker reason more precisely about method calls to solve the nested argument problem in its most common incarnation.

One could imagine saying that a.method(...) evaluates a last, and that might just mean that transforming to Trait::method(a, ...) form isn’t always valid without more complex transformations to preserve execution order. But I find it very surprising that a.foo(..) wouldn’t evaluate a first. I think it’s because the receiver is so primary in method dispatch.

Assignments are rather different, both because things like vec[i] = vec[j] are pretty common and because the evaluation order is evident from the surface syntax, and there IS a certain logic to “evaluate the value you are going to store first, and then find the place you are going to store it into”.


#24

As written, I agree with that instinct. Then again in a call like a.b.f(a.g()) (where a.g() takes &mut self) I’d be surprised that the inner function call can’t be executed, because a is already borrowed. Which probably goes to say that at least I personally always expect inner to outer, as well as left to right, with inner to outer having a higher precedence (which is probably derived from math’s brackets first rule)…


#25

The interesting thing is more like a.get().f(a.g()) - should get be called before g?

When user-defined autoderef is involved, it is not exactly possible to just “reason more precisely about method calls” - as autoderef is allowed to e.g. look at the interior of RefCell-s without marking them as ref-ed (and that can be invalidated by &-borrowing code).

“upgradeable” references would solve this issue, but these require lots of work.


#26

bump.


#27

What’s the current state regarding order of evaluation? It is very tempting to write this:

struct Item {
    a: u32,
    b: u32,
}

impl Item {
    fn receive_word(&mut self) -> Result<u32, Error> {
        …
    }

    fn receive(&mut self) -> Result<Item, Error> {
        Ok(Item {
            a: self.receive_word()?,
            b: self.receive_word()?,
        })
    }
}

The expectation is that first the value a is received, then the value b. But with a non-determinate evaluation order, one has to introduce temporaries.


#28

Even with temporaries, it doesn’t look too bad in my opinion:

    fn receive(&mut self) -> Result<Item, Error> {
        let a = self.receive_word()?;
        let b = self.receive_word()?;
        Ok(Item { a, b })
    }

#29

That code is correct and there is no chance it will change. In fact, I’m more or less of the opinion that the ship has sailed with respect to making changes to order of evaluation, period.

Nonetheless, the cases that were somewhat in question had to do with things like precisely when the index was computed in an expression like this:

x[x[i]] += x[i]

Here there are some slight inconsistencies between overloaded operators and non-overloaded ones and so forth. But if you’re writing “readable code”, you’ll never notice.

UPDATE: To be clear, that example was from memory, I’d have to go lookup the tricky cases…


#30

More to the point, in a struct literal, the fields are evaluated in the order you write them; if a panic occurs before the struct is completely build, the intermediate values are dropped in the reverse order (once the struct is fully built, the fields are dropped in the order they are written in the struct declaration, iirc).


#31

Hi, are there some docs somewhere on the currently existing evaluation order?


#32

Not that I know of. I would like to get more progress on a reference of this kind. Roughly speaking, the order is left-to-right, though in an assignment l = r, the expression l is evaluated second.


#33

Filed https://github.com/rust-lang-nursery/reference/issues/248