6.1Operator precedence and associativity

Last updated June 12, 2026

Lesson 1.10 introduced operators and made a promise: the full precedence rules arrive in chapter 6, along with the only advice about them worth memorizing. This is chapter 6. Time to pay up.

You've been using operators on credit for five chapters now: + since your first program, == and < since lesson 4.6, % in passing whenever evenness came up. This chapter gives each of them a proper introduction. First, though, we need to settle a question that affects all of them at once: when several operators appear in the same expression, who goes first?

Precedence

Consider:

fn main() {
    let x = 2 + 3 * 4;
    println!("{x}");
}
14

You learned the answer in grade school: multiplication before addition, so this is 2 + (3 * 4), not (2 + 3) * 4. The compiler agrees, but not because it remembers grade school. Every operator in Rust has a rank called its precedence, and when two operators compete for the same operand, the higher-precedence operator wins it. * outranks +, so * gets the 3.

Associativity

Precedence can't settle everything. What about 7 - 4 - 1, where both operators are the same operator?

fn main() {
    println!("{}", 7 - 4 - 1);
}
2

When operators of equal precedence are adjacent, the tie is broken by associativity: the rule saying whether grouping proceeds from the left or from the right. Subtraction associates left-to-right, so this is (7 - 4) - 1, which is 2. Right-to-left grouping would have produced 7 - (4 - 1), which is 4, and a lot of wrong change.

The table

Here is the ranking, from grabbiest to most patient. Rows you haven't met yet are marked with where they're coming from; skim those for now.

PrecedenceOperatorsAssociativity
highestmethod calls, field access (value.abs(), pair.0)left-to-right
unary -, unary !
as (lesson 4.10)left-to-right
* / %left-to-right
+ -left-to-right
<< >> (lesson 6.6)left-to-right
& (lesson 6.6)left-to-right
^ (lesson 6.6)left-to-right
| (lesson 6.6)left-to-right
== != < > <= >=none: chaining is an error
&& (lesson 6.5)left-to-right
|| (lesson 6.5)left-to-right
lowest= += -= and family (lesson 6.3)right-to-left

You are not expected to memorize this table, and that's not false modesty. Professional Rust programmers don't memorize it either; they remember three facts and parenthesize everything else. The three facts: arithmetic works like math class, comparisons happen after arithmetic, and assignment happens last.

Best practice

Use parentheses to make non-trivial expressions unmistakable, even where precedence already does what you want. The exception: ordinary arithmetic (+, -, *, /) can go bare, since every reader knows those rules. When in doubt, parenthesize. That's the advice lesson 1.10 promised, and it's the whole of it.

Comparisons don't chain

One row of the table deserves its own section, because it has no associativity at all. In math, 1 < 2 < 3 is a perfectly good statement. Try it in Rust:

fn main() {
    let ordered = 1 < 2 < 3;
    println!("{ordered}");
}
error: comparison operators cannot be chained
 --> src/main.rs:2:21
  |
2 |     let ordered = 1 < 2 < 3;
  |                     ^   ^
  |
help: split the comparison into two
  |
2 |     let ordered = 1 < 2 && 2 < 3;
  |                         ++++

The compiler refuses, and offers the fix in the same breath (&& means "and both"; it gets its full lesson in 6.5). To appreciate what a kindness this refusal is: C and C++ accept 1 < 2 < 3 and evaluate it as (1 < 2) < 3, which compares true against 3, which is essentially never what anyone meant, and which compiles without a murmur. Rust looked at that piece of inherited behavior and declined.

Precedence is not evaluation order

Here's the subtlety that trips up people who did memorize the table. Precedence decides how results are grouped. It does not decide the order in which the operands themselves get computed. Watch:

fn first() -> i32 {
    println!("first() runs");
    2
}

fn second() -> i32 {
    println!("second() runs");
    3
}

fn third() -> i32 {
    println!("third() runs");
    4
}

fn main() {
    let result = first() + second() * third();
    println!("{result}");
}
first() runs
second() runs
third() runs
14

The multiplication groups tighter, but first() still runs first. Operands evaluate left to right, in the order written; precedence only governs how the finished values combine.

Key insight

In Rust, left-to-right operand evaluation is a guarantee of the language, and the same guarantee covers function arguments. In C and C++ this order is unspecified, the compiler may compute second() before first(), and an entire genre of bugs lives in the gap. Rust deleted the genre. You still shouldn't lean on the guarantee for anything clever: if the order of side effects matters, separate statements say so far more legibly than an expression does.

Quiz time

Question #1

For each expression, add parentheses showing how the compiler groups it. (Peek at the table; that's what it's for.)

a) 4 + 3 - 2 + 1 b) 6 - 2 * 3 % 4 c) 1 + 2 == 9 / 3

Show solution

a) ((4 + 3) - 2) + 1 (equal precedence, left-to-right) b) 6 - ((2 * 3) % 4) (* and % share a rank above -, and group left-to-right between themselves) c) (1 + 2) == (9 / 3) (all arithmetic settles before any comparison: 3 == 3, so true)

Question #2

What does this print, and in what order do the three lines appear?

fn left() -> i32 {
    println!("left");
    10
}

fn right() -> i32 {
    println!("right");
    5
}

fn main() {
    println!("{}", left() - right());
}
Show solution
left
right
5

Operands evaluate left to right, guaranteed, so left prints before right; then 10 - 5 is 5.

Question #3

Predict the compiler's reaction:

fn main() {
    let x = 5;
    let in_range = 1 <= x <= 10;
    println!("{in_range}");
}
Show solution

error: comparison operators cannot be chained. Comparisons have no associativity in Rust; a chain is a compile error, not a misunderstanding. The compiler's help: suggests the repair, which lesson 6.5 will make official: 1 <= x && x <= 10.

Next up: formal introductions for the arithmetic crew, including the full story of integer division that lesson 4.5 left hanging.