R
RUSTY YELLOW PAGES

+=Operator


Explanation

+= adds the right operand to the left in place, overloadable via std::ops::AddAssign.

x += 1 is not always exactly sugar for x = x + 1 — types can implement AddAssign differently from Add (e.g. to mutate in place without an extra allocation, which matters for types like String or Vec), though for simple numeric types the two behave identically. The left operand must be a mutable place — x must be declared let mut x.

Usage examples

Adding to a mutable binding in place

let mut x = 5;
x += 1; // <- `+=` adds the right operand into `x` in place

Restriction: the left operand must be a mutable place — x has to be declared with let mut x, or this won't compile.

Modifying an existing object

An account balance is a natural home for += — the balance is a field mutated in place each time a deposit posts, rather than a whole new Account being constructed per transaction.

struct Account {
    balance: i64, // cents
}

impl Account {
    fn deposit(&mut self, cents: i64) {
        self.balance += cents; // <- `+=` mutates the field in place
    }
}

let mut checking = Account { balance: 10_000 };
checking.deposit(2_500);
checking.deposit(750);
assert_eq!(checking.balance, 13_250);

A &mut self method that uses += on its own field keeps the mutation local and auditable — the alternative of returning a new Account on every deposit would work but adds ceremony for a value type that's meant to change over its lifetime. The &mut self method syntax itself is covered in the Book.

Working with collections

Accumulating a running total while iterating is the textbook use of +=; it's worth contrasting with re-binding a fresh let total = ... on every loop pass, which doesn't compile without mut and wouldn't express "the same total, growing" as directly even if it did.

let orders = [1200, 450, 899, 3000]; // cents

let mut total = 0; // <- `mut` binding `+=` will mutate
for cents in orders {
    total += cents; // <- `+=` folds each order into the running total
}
assert_eq!(total, 5549);

// Equivalent, more idiomatic for a one-shot fold:
let total: i32 = orders.iter().sum();

The explicit for loop with += is the clearest form when the accumulation is interleaved with other per-item work; when summing is the only thing happening, Iterator::sum from the standard library docs expresses the same intent with no mutable state at all.

Explanation

Embedded support: Full

AddAssign lives in core::ops, so += behaves identically under #![no_std]. The relevant nuance is the same one covered on +: a release build ships with overflow checks off, so += wraps silently on overflow instead of panicking, and a device already deployed can't be recompiled in debug to catch it after the fact. += on a value that's provably bounded within a single function call — summing a handful of samples into a wider accumulator type, say — is perfectly safe to leave as bare +=. += on a value that accumulates for the entire lifetime of the device — an uptime counter, an event tally, a running energy total — is exactly the spot where an explicit wrapping_add/checked_add assigned back is worth the extra line, since that's where a silent wrap would otherwise go unnoticed indefinitely.

Usage examples

Using `+=` for a bounded per-cycle accumulator

fn average_adc_reading(samples: &[u16; 8]) -> u16 {
    let mut sum: u32 = 0;
    for &s in samples {
        sum += s as u32; // <- `+=` is fine here: 8 u16 samples can never overflow a u32 accumulator
    }
    (sum / samples.len() as u32) as u16
}

Guarding a long-lived event counter instead of bare `+=`

struct EventCounter(u32);

impl EventCounter {
    fn record(&mut self) {
        self.0 = self.0.wrapping_add(1); // explicit wrapping stands in for a bare `+=` that would wrap unnoticed anyway
    }
}