Skip to content

Rust Error E0368: Binary Assignment Operation += Cannot Be Applied to Type

E0368 means you used a compound operator like +=, -= or *= on a type that doesn’t support it yet. It’s the += cousin of E0369. Your own structs don’t get += automatically, not even when they already support +. The fix is usually a short impl AddAssign block, and on numbers behind a reference it’s a single *. For the other errors in this series, see Rust compiler errors.

error[E0368]: binary assignment operation `+=` cannot be applied to type `Money`
--> src/main.rs:8:5
|
8 | wallet += tip; // ❌ Money doesn't know how to do +=
| ------^^^^^^^
| |
| cannot use `+=` on type `Money`
|
note: an implementation of `AddAssign` might be missing for `Money`
--> src/main.rs:1:1
|
1 | struct Money {
| ^^^^^^^^^^^^ must implement `AddAssign`

This is the program that produces it:

struct Money {
cents: i64,
}
fn main() {
let mut wallet = Money { cents: 500 };
let tip = Money { cents: 150 };
wallet += tip; // ❌ Money doesn't know how to do +=
println!("{}", wallet.cents);
}

The operator in the first line changes, but it’s the same error. p -= step asks for SubAssign, and p *= 2 asks for MulAssign<{integer}>, which means “multiply-assign by a whole number”.

The label under the left side says what went wrong: “cannot use += on type Money”. The note: then points at your struct and says it must implement AddAssign. That’s the trait behind +=.

Think of a piggy bank. Dropping a coin in is a clear action. But if someone hands you a box labelled “Money” and says “add this other box to it”, you need to know how: add the cents together? stack the boxes? Rust needs you to write that rule down once.

Every compound operator is a method call in disguise. a += b calls AddAssign::add_assign(&mut a, b), a -= b calls SubAssign::sub_assign, and so on. All these traits live in std::ops. Numbers implement them already. Your new struct starts with none of them, and there is no #[derive] for math operators.

Here’s the part that surprises people: Add and AddAssign are separate traits. Even if Point already implements Add, p += q still gives E0368. The reason is that they do different jobs. a + b takes two values and returns a new one. a += b changes a in place through &mut self and returns nothing. In-place can be much cheaper: String’s += appends to the existing buffer instead of building a new string. Rust lets each type choose, so it never quietly turns += into + for you.

Operator traits are explained in Common traits and Advanced traits.

Choose this when += on your type has a clear meaning, like adding money or moving a position. You write one method that changes self. Add SubAssign, MulAssign and the others the same way when you need them:

use std::ops::{AddAssign, SubAssign};
#[derive(Debug)]
struct Money {
cents: i64,
}
impl AddAssign for Money {
fn add_assign(&mut self, other: Money) {
self.cents += other.cents;
}
}
impl SubAssign for Money {
fn sub_assign(&mut self, other: Money) {
self.cents -= other.cents;
}
}
fn main() {
let mut wallet = Money { cents: 500 };
wallet += Money { cents: 150 }; // got a tip
wallet -= Money { cents: 200 }; // bought lunch
println!("{:?}", wallet);
}

Output:

Money { cents: 450 }
▶ Try it Yourself

Notice &mut self and no return type. That’s the whole difference from Add. Inside, self.cents += other.cents works because i64 already has AddAssign.

Fix 2: Already have Add? Write a = a + b, or reuse it

Section titled “Fix 2: Already have Add? Write a = a + b, or reuse it”

Choose this when your type already implements Add (maybe after fixing E0369). If the type is Copy, you can simply write pos = pos + step, which needs only Add. Or implement AddAssign in one line by calling your Add, so += works too:

use std::ops::{Add, AddAssign};
#[derive(Debug, Clone, Copy)]
struct Point {
x: i32,
y: i32,
}
impl Add for Point {
type Output = Point;
fn add(self, other: Point) -> Point {
Point { x: self.x + other.x, y: self.y + other.y }
}
}
impl AddAssign for Point {
fn add_assign(&mut self, other: Point) {
*self = *self + other; // reuse Add
}
}
fn main() {
let mut pos = Point { x: 0, y: 0 };
let step = Point { x: 1, y: 2 };
pos = pos + step; // needs only Add
pos += step; // needs AddAssign
println!("{:?}", pos);
}

Output:

Point { x: 2, y: 4 }
▶ Try it Yourself

*self = *self + other copies the current value out, adds, and writes the result back. That only works because Point is Copy. For a type that owns heap data (like a Vec field), write the in-place version from Fix 1 instead.

Choose this when the left side is a string. += works on an owned String, and the right side must be a &str. Two cases trip people up:

  • cannot be applied to type `&str`: the left side is a string slice like "Hello, ". A &str is a read-only view, so it can’t grow. Make it a String first.
  • s += t where t is also a String is not E0368. It’s E0308, “expected &str, found String”, with the help s += &t. Just borrow the right side.
fn main() {
let mut greeting = String::from("Hello, "); // owned, so it can grow
let name = String::from("Ferris");
greeting += &name; // borrow the right side
greeting.push_str("!"); // the same thing, as a method
let mut tag = "crab".to_string(); // &str on the left? make it a String
tag += " #1";
println!("{greeting}");
println!("{tag}");
println!("{name} is still usable");
}

Output:

Hello, Ferris!
crab #1
Ferris is still usable
▶ Try it Yourself

Because the right side is only borrowed, name is still yours afterwards. += and push_str do exactly the same thing; use whichever reads better. See Strings.

Fix 4: Add a T: AddAssign bound in generic code

Section titled “Fix 4: Add a T: AddAssign bound in generic code”

Choose this when the error says cannot be applied to type `T`. Inside a generic function, T could be any type, even one without +=. rustc’s help says exactly what to add: consider further restricting type parameter `T` with trait `AddAssign`.

use std::ops::AddAssign;
fn total<T>(items: &[T]) -> T
where
T: Copy + Default + AddAssign,
{
let mut sum = T::default();
for &item in items {
sum += item;
}
sum
}
fn main() {
println!("{}", total(&[1, 2, 3]));
println!("{}", total(&[0.5, 0.25]));
}

Output:

6
0.75
▶ Try it Yourself

Now total works for any type that can be copied, has a starting value and supports +=: integers, floats, and your own Money once it implements those traits. For plain numbers, items.iter().sum() does the same job in one line. See Generics.

Counting with a HashMap. let count = counts.entry(word).or_insert(0); then count += 1; gives cannot be applied to type `&mut i32`. count is a reference, not the number. rustc’s help: “+= can be used on i32 if you dereference the left-hand side”. Fix: *count += 1;. See HashMaps.

Scaling a struct by a number. p *= 2 needs MulAssign<{integer}>, because the right side is a number, not another Point. Fix: implement the trait for that right-hand type:

impl std::ops::MulAssign<i32> for Point {
fn mul_assign(&mut self, factor: i32) {
self.x *= factor;
self.y *= factor;
}
}

Running totals in a loop. total += item.price where price is your own Money type. Fix 1 makes it work. If you’d rather write prices.iter().copied().sum(), that needs the Sum trait instead, and without it you get E0277 (“a value of type Money cannot be made by summing an iterator”).

Quick check

1. Point implements Add, but p += q still gives E0368. Why?

2. count comes from map.entry(word).or_insert(0). Which line compiles?

3. s and t are both String. How do you append t to s?

Score: 0 / 3