Skip to content

Clarify operand evaluation order for a += b with primitive vs generic types #1940

@fiveseven-lambda

Description

@fiveseven-lambda

The section on compound assignment expressions currently states:

If both types are primitives, then the modifying operand will be evaluated first followed by the assigned operand.

This suggests that when both operands are primitive types, the right-hand side is always evaluated before the left-hand side.

However, this behavior is not preserved when using generic code, even with primitive types. Consider the following example:

fn main() {
    println!("i32, i32: {:?}", order_i32());
    println!("i32, i32 (Generic): {:?}", order::<i32, i32>());
    println!("String, &str (Generic): {:?}", order::<String, &str>());
}

fn order_i32() -> Vec<&'static str> {
    let mut order = vec![];
    {
        order.push("left");
        (0,)
    }
    .0 += {
        order.push("right");
        0
    };
    order
}

fn order<T, U>() -> Vec<&'static str>
where
    T: Default,
    U: Default,
    T: std::ops::AddAssign<U>,
{
    let mut order = vec![];
    {
        order.push("left");
        (T::default(),)
    }
    .0 += {
        order.push("right");
        U::default()
    };
    order
}

Output:

i32, i32: ["right", "left"]
i32, i32 (Generic): ["left", "right"]
String, &str (Generic): ["left", "right"]

This shows that even when both operands are i32, the evaluation order is reversed if the operation is generic (i.e., uses the AddAssign trait explicitly). This contradicts the current documentation, or at least suggests that it is oversimplified.

Suggested improvement:

Clarify that:

  • The "right-then-left" evaluation order only applies to non-generic code involving primitive types, due to compiler desugaring.
  • In generic contexts (even with primitive types), a += b behaves like a method call to AddAssign::add_assign, and evaluates operands left-to-right, as with most Rust expressions.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions