6.3Compound assignment: the missing ++
Back in lesson 1.10, a warning box used count += 1 and attached an IOU reading "compound assignment, lesson 6.3". Here we are.
The most common thing programs do to a variable is fold something into it: add to a total, double a score, knock a balance down. Spelled with what you know so far, that's the variable's name written twice:
fn main() {
let mut total = 100;
total = total + 25;
println!("{total}");
}125
It works, but the repetition is noise, and for once the shortcut is older than the long way. A compound assignment operator applies an arithmetic operator and an assignment in one stroke:
| Operator | This... | ...means this |
|---|---|---|
+= | x += y | x = x + y |
-= | x -= y | x = x - y |
*= | x *= y | x = x * y |
/= | x /= y | x = x / y |
%= | x %= y | x = x % y |
(The bitwise operators in lesson 6.6 have compound forms too: &=, |=, ^=, <<=, >>=. Same idea.)
fn main() {
let mut total = 100;
total += 25;
total -= 5;
total *= 2;
println!("{total}");
}240
Since these assign, the variable must be mut, and all of lesson 1.4's rules apply. And like plain assignment (lesson 1.10), a compound assignment is an expression whose value is the unit type (), not the new value of the variable. You can't chain it, can't print it, can't smuggle it into a condition: the if x = 5 mistake that lesson 4.7 showed you stays a type error in compound form. Rust assignments do their job and say nothing.
Best practice
When the left side would repeat, use the compound form: total += tax, not total = total + tax. It's shorter, it can't misspell the second mention of the name, and every reader's eye is trained on it.
Where's ++?
If you've seen C, C++, Java, or JavaScript, you know those languages bless the +1 case with dedicated operators: ++ to increment, -- to decrement. Rust does not have them. This was a decision, not an oversight, and it's worth a minute, because the reasoning is a small window into how Rust weighs convenience against trouble.
C's ++ isn't one operator; it's four. ++x and x++ both add 1, but they're expressions with different values: ++x evaluates to the value after the increment, x++ to a copy of the value before it. Every C programmer has burned an afternoon on that distinction. Worse, because each one is an expression carrying a side effect, it invites being nested inside larger expressions, where it collides with the operand-evaluation-order problem from lesson 6.1: a C call like add(x, ++x) can produce different results on different compilers. Two characters of convenience, three categories of bug.
Rust's position: incrementing is a statement, not a value. Write x += 1; and move on. The price is two keystrokes; the refund is that an entire family of interview questions stops compiling:
fn main() {
let mut x = 5;
x++;
println!("{x}");
}error: Rust has no postfix increment operator
--> src/main.rs:3:6
|
3 | x++;
| ^^ not a valid postfix operator
|
help: use `+= 1` instead
|
3 - x++;
3 + x += 1;
|
The compiler knows exactly what you reached for and hands you the house version.
Warning
The decrement spelling is sneakier. --x is not an error: it parses as two unary minuses, "negate the negation of x", a perfectly legal expression that equals x and changes nothing. The compiler does notice the suspicious shape and warns (use of a double negation, with the note "use -= 1 if you meant to decrement the value"), but a warning won't stop the build: code migrated from C can still run with a --x that quietly stopped decrementing. If a countdown won't count down, check the warnings you skimmed past (lesson 0.11's sermon, proven right again).
Quiz time
Question #1
What does this print? Trace it line by line.
fn main() {
let mut n = 7;
n *= 3;
n -= 1;
n %= 6;
n += 10;
println!("{n}");
}Show solution
12
7 * 3 is 21; minus 1 is 20; 20 % 6 is 2 (three sixes fit, remainder 2); plus 10 is 12.
Question #2
Predict the compiler's reaction:
fn main() {
let mut lives = 3;
++lives;
println!("{lives}");
}Show solution
error: Rust has no prefix increment operator, with the same help (use += 1 instead) the postfix version gets. (Prefix gets caught because a leading ++ can't be parsed as anything legal; contrast the --x trap from this lesson's warning box, which compiles as a double negation and only draws a warning.)
Question #3
This fragment arrived from C. Rewrite the marked lines as idiomatic Rust, keeping the behavior.
score = score + level * 10;
bonus = bonus - 1;
combo = combo * 2;Show solution
score += level * 10;
bonus -= 1;
combo *= 2;
Note the first one: * outranks +='s implied addition, so score += level * 10 adds the product, exactly like the original. Compound assignment has the lowest precedence of all, per lesson 6.1's table: everything on the right settles first.
So far the operators in this chapter have computed numbers. The next two lessons cover the operators that compute answers: true or false, including (at last) the float-comparison tool lesson 4.5 promised you.