Rust Error E0369: Binary Operation Cannot Be Applied to Type
E0369 means you used an operator like ==, < or + on a type that doesn’t know how to do it yet. In Rust, operators are not built into your own structs and enums. Each one comes from a trait, and your type only gets it when you derive or implement that trait. The fix is usually a single #[derive(...)] line, and the compiler even tells you which one. For the other errors in this series, see Rust compiler errors.
The error message
Section titled “The error message”error[E0369]: binary operation `==` cannot be applied to type `Point` --> src/main.rs:9:10 |9 | if a == b { // ❌ Point doesn't know how to compare itself | - ^^ - Point | | | Point |note: an implementation of `PartialEq` might be missing for `Point` --> src/main.rs:1:1 |1 | struct Point { | ^^^^^^^^^^^^ must implement `PartialEq`help: consider annotating `Point` with `#[derive(PartialEq)]` |1 + #[derive(PartialEq)]2 | struct Point { |This is the program that produces it:
struct Point { x: i32, y: i32,}
fn main() { let a = Point { x: 1, y: 2 }; let b = Point { x: 1, y: 2 }; if a == b { // ❌ Point doesn't know how to compare itself println!("Same point!"); }}The first line changes with the operator, but it is the same error:
binary operation `<` cannot be applied to type `Version`: missingPartialOrd(also for>,<=,>=).cannot add `Point` to `Point`: missingAdd. You’ll also see “cannot subtract”, “cannot multiply”, and so on.cannot add `&str` to `&str`: you tried to join two string slices with+.
What it means
Section titled “What it means”The labels under the operator show the type on each side, here Point and Point. Then the note: points at your struct and says it must implement PartialEq. In other words: “you asked me to compare two Points, but nobody told me what equal means for a Point.”
Think of a sports referee who is handed two objects and asked “which is bigger?”. For numbers the answer is obvious. For two backpacks, the referee needs a rule first: by weight? by volume? Rust is that careful referee.
Why Rust does this
Section titled “Why Rust does this”Every operator is a method call in disguise. a == b calls PartialEq::eq, a < b calls PartialOrd::partial_cmp, and a + b calls Add::add. Numbers, String and many standard types implement these traits already. Your new struct starts with none of them.
Rust doesn’t guess, because guessing would be wrong sometimes. Should two Users be equal when their IDs match, or only when every field matches? Should Point + Point add the coordinates, or is adding points even meaningful? You decide by deriving the trait (the field-by-field default) or by writing your own impl. The traits are explained on the Common traits page.
How to fix it
Section titled “How to fix it”Fix 1: #[derive(PartialEq)] for == and !=
Section titled “Fix 1: #[derive(PartialEq)] for == and !=”Choose this for == and != when “equal” means “all fields are equal”. This is the right fix most of the time, and it is exactly what rustc suggests. Adding Debug too lets you print the struct:
#[derive(Debug, PartialEq)]struct Point { x: i32, y: i32,}
fn main() { let a = Point { x: 1, y: 2 }; let b = Point { x: 1, y: 2 }; let c = Point { x: 5, y: 0 }; if a == b { println!("Same point!"); } println!("{:?} != {:?}: {}", a, c, a != c);}Output:
Same point!Point { x: 1, y: 2 } != Point { x: 5, y: 0 }: trueEvery field must be comparable too. If a field’s type lacks PartialEq, derive it on that type as well. Enums work the same way: #[derive(PartialEq)] on enum Status makes status == Status::Banned compile.
Fix 2: #[derive(PartialOrd)] for < and >
Section titled “Fix 2: #[derive(PartialOrd)] for < and >”Choose this when your type has a natural order. PartialOrd needs PartialEq, so rustc suggests #[derive(PartialEq, PartialOrd)]. The derived version compares fields from top to bottom: first major, and only if those are equal, minor. So the field order in your struct matters:
#[derive(Debug, PartialEq, PartialOrd)]struct Version { major: u32, minor: u32,}
fn main() { let installed = Version { major: 1, minor: 4 }; let required = Version { major: 1, minor: 7 }; if installed < required { println!("Please update from {:?}", installed); }
let next = Version { major: 2, minor: 0 }; println!("2.0 is newer than 1.7: {}", next > required);}Output:
Please update from Version { major: 1, minor: 4 }2.0 is newer than 1.7: trueIf you want to .sort() a Vec of your type, you’ll need Eq and Ord as well. See Sorting.
Fix 3: impl std::ops::Add for +
Section titled “Fix 3: impl std::ops::Add for +”Choose this when adding two values of your type has a clear meaning, like adding coordinates or money. There is no derive for math operators, so you write a short impl. Output is the type the + gives back:
use std::ops::Add;
#[derive(Debug, Clone, Copy, PartialEq)]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 } }}
fn main() { let start = Point { x: 1, y: 2 }; let step = Point { x: 3, y: 4 }; let end = start + step; println!("{:?}", end); println!("{}", end == Point { x: 4, y: 6 });}Output:
Point { x: 4, y: 6 }trueSub, Mul, Div and Neg work the same way, all in std::ops. Deriving Copy here means start and step are still usable after the +. More in Common traits and Advanced traits.
Fix 4: Joining strings with + or format!
Section titled “Fix 4: Joining strings with + or format!”Choose this when the error says cannot add `&str` to `&str`. + on strings needs an owned String on the left and a &str on the right. rustc’s help says it in its own words: “create an owned String from a string reference”, so first.to_owned() + second. With two Strings, borrow the right one: s1 + &s2. And format! always works and doesn’t move anything:
fn main() { let first = String::from("Hello, "); let second = String::from("Ferris");
let shout = format!("{first}{second}!"); // borrows both let joined = first + &second; // first is moved into joined let short = "Hi, ".to_owned() + "crab"; // &str on the left: make it owned first
println!("{shout}"); println!("{joined}"); println!("{short}"); println!("{second} is still here");}Output:
Hello, Ferris!Hello, FerrisHi, crabFerris is still here+ takes ownership of the left String and appends to it, which is why first can’t be used after that line. If that’s confusing, just use format!. See Strings.
Where you’ll usually see it
Section titled “Where you’ll usually see it”Comparing in generic code. The classic largest function fails with binary operation `>` cannot be applied to type `&T`, because T could be any type, even one without an order:
fn largest<T>(list: &[T]) -> &T { let mut largest = &list[0]; for item in list { if item > largest { // ❌ T might not be comparable largest = item; } } largest}Fix: add a trait bound, exactly as rustc suggests: fn largest<T: PartialOrd>(list: &[T]) -> &T. See Generics.
Checking an enum value. if status == Status::Banned fails until the enum has #[derive(PartialEq)]. If you can’t change the enum (it comes from another crate), use matches!(status, Status::Banned) or a match instead.
Joining two Strings. first + second with two Strings is not E0369: it is E0308 (“expected &str, found String”) with help: consider borrowing here, first + &second. Same family, same fix as Fix 4.
Comparing two different types. count == limit with an i32 and an f64 gives E0308, and name == 'F' with a String and a char gives E0277 (“can’t compare String with char”). A rule of thumb: you get E0369 when the left type has no version of the operator at all, and E0308 or E0277 when it has one, but not for that right-hand type. Fix: convert one side (count as f64 == limit, name == "F").
Related errors
Section titled “Related errors”- E0277: trait bound not satisfied: the operator exists for the left type, but not with this right-hand type (
i32 + f64,String == char). - E0308: mismatched types: what you get for
String + Stringor for comparing two different structs. - E0599: no method found: calling
.sort()or.contains()can fail the same way when your type is missingOrdorPartialEq. E0368: binary assignment operation += cannot be applied: the+=version of this error. ImplementAddAssignfromstd::ops.- All compiler errors: the full list of error pages.
Quick check
Section titled “Quick check”Quick check
1. You write if order_a == order_b for a struct Order and get E0369. What is usually the fix?
2. With #[derive(PartialOrd)] on struct Version { major: u32, minor: u32 }, how are two versions compared?
3. first and second are both &str. Which expression joins them without an error?
Score: 0 / 3
Read more
Section titled “Read more”- E0369 — Rust error codes index: the official explanation of this error.
- Derivable Traits — The Rust Book, Appendix C: what deriving
PartialEqandPartialOrdgives you, including the field-by-field order. - Storing UTF-8 Encoded Text with Strings — The Rust Book: why
+on strings takes aStringon the left and&stron the right, and when to useformat!. - Operator Overloading — Rust by Example: a short runnable example of implementing
Addfor your own types. std::ops::Add— standard library docs: theAddtrait, itsOutputtype, and adding values of different types.