Skip to content

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.

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`: missing PartialOrd (also for >, <=, >=).
  • cannot add `Point` to `Point`: missing Add. You’ll also see “cannot subtract”, “cannot multiply”, and so on.
  • cannot add `&str` to `&str`: you tried to join two string slices with +.

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.

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.

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 }: true
▶ Try it Yourself

Every 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.

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: true
▶ Try it Yourself

If you want to .sort() a Vec of your type, you’ll need Eq and Ord as well. See Sorting.

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 }
true
▶ Try it Yourself

Sub, 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.

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, Ferris
Hi, crab
Ferris is still here
▶ Try it Yourself

+ 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.

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").

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