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.
The error message
Section titled “The error message”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”.
What it means
Section titled “What it means”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.
Why Rust does this
Section titled “Why Rust does this”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.
How to fix it
Section titled “How to fix it”Fix 1: Implement AddAssign (and friends)
Section titled “Fix 1: Implement AddAssign (and friends)”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 }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 }*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.
Fix 3: Strings: s += &t or push_str
Section titled “Fix 3: Strings: s += &t or push_str”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&stris a read-only view, so it can’t grow. Make it aStringfirst.s += twheretis also aStringis not E0368. It’s E0308, “expected&str, foundString”, with the helps += &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 #1Ferris is still usableBecause 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]) -> Twhere 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:
60.75Now 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.
Where you’ll usually see it
Section titled “Where you’ll usually see it”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”).
Related errors
Section titled “Related errors”- E0369: binary operation cannot be applied: the same problem for
+,==,<and the other non-assigning operators. - E0308: mismatched types: what you get for
s += twith twoStrings (borrow it:s += &t). - E0277: trait bound not satisfied:
+=exists for the left type, but not with this right-hand type, likecannot add-assign `{float}` to `{integer}`fortotal += 2.5whentotalis an integer. - E0384: cannot assign twice:
x += 1on a variable declared withoutmut. The operator is fine, the variable just isn’t mutable. - All compiler errors: the full list of error pages.
Quick check
Section titled “Quick check”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
Read more
Section titled “Read more”- E0368 — Rust error codes index: the official explanation of this error.
std::ops::AddAssign— standard library docs: the trait behind+=, with an example;SubAssign,MulAssignand the rest are listed instd::ops.- Advanced Traits — The Rust Book: operator overloading with
std::opsand default type parameters likeRhs. - Storing UTF-8 Encoded Text with Strings — The Rust Book: why strings grow with
push_strand+takes a&stron the right. - Operator Overloading — Rust by Example: a short runnable example of implementing operator traits for your own types.