Skip to content
rusthacks.dev
Generics

Rust Error E0277 Trait Bound Not Satisfied and the Fix

E0277 means a type does not implement a trait that the code requires. Read the required-by note, then add a derive, an impl or a bound.

7 min read
Summarize in AI
Flat illustration for the post “Rust Error E0277 Trait Bound Not Satisfied and the Fix”, composed around generics.

error[E0277]: the trait bound ... is not satisfied is the most general error in Rust. It means some code requires a type to implement a trait, and the type you gave it does not. The wording changes with the trait: "doesn't implement Display", "cannot be sent between threads safely", "cannot be indexed by i32", "the ? operator can only be used in a function that returns Result". Underneath they are one error, and the fix is one of two moves: implement the missing trait, or change the type to one that already does. This post walks through the five forms you will meet, with the compiler's real output from Rust 1.99, in the order of how often they occur.

How to read any E0277

The error has the same anatomy every time. The first line names the type and the trait. A line with a caret points at the code that required the trait, often a function call or a format string. A required by a bound introduced by this call or a note: required by a bound in ... line points at the function signature that demanded it. And a help line often names the fix, a derive or an impl. Read in that order: what is missing, where it was demanded, what to do.

Form one: the question mark in a function that returns nothing

The shortest reproduction is a main that uses ? and returns ():

src/main.rsrust
use std::fs;

fn main() {
    let text = fs::read_to_string("config.toml")?;
    println!("{}", text.len());
}
Text
error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`)
 --> a277.rs:4:49
  |
3 | fn main() {
  | --------- this function should return `Result` or `Option` to accept `?`
4 |     let text = fs::read_to_string("config.toml")?;
  |                                                 ^ cannot use the `?` operator in a function that returns `()`
  |
help: consider adding return type
  |
3 ~ fn main() -> Result<(), Box<dyn std::error::Error>> {
4 |     let text = fs::read_to_string("config.toml")?;
5 |     println!("{}", text.len());
6 +     Ok(())

The ? operator returns early with the error, so the enclosing function must return a type that can hold one. The compiler's suggestion is correct and complete: change the signature and end with Ok(()). Run the fixed program in a directory without the file and the error surfaces as a message and a non-zero exit status:

Text
Error: Os { code: 2, kind: NotFound, message: "No such file or directory" }

That is the Debug form of the io::Error, printed by main on the way out, which is good enough for small tools. For a program with users, convert errors into a readable message; error handling in Rust covers anyhow and thiserror for that.

Form two: printing a type that has no Display

{} calls the Display trait, and your struct does not implement it:

Text
error[E0277]: `Point` doesn't implement `std::fmt::Display`
 --> b277.rs:8:20
  |
8 |     println!("{}", p);
  |               --   ^ `Point` cannot be formatted with the default formatter
  |
help: the trait `std::fmt::Display` is not implemented for `Point`
  = note: in format strings you may be able to use `{:?}` (or `{:#?}` for pretty-print) instead

There are two fixes, and they mean different things. If you want a debugging dump, derive Debug and print with {:?}. If you want a user-facing format, implement Display yourself, because the compiler cannot know how a point should look:

src/main.rsrust
use std::fmt;

impl fmt::Display for Point {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "({}, {})", self.x, self.y)
    }
}
Text
(1, 2)

Display has no derive on purpose: text meant for people needs a human decision, while Debug is a mechanical dump.

Form three: a key that cannot be hashed

Using your own type as a HashMap key requires Hash and Eq:

Text
error[E0277]: the trait bound `Key: Hash` is not satisfied
 --> c277.rs:8:7
  |
8 |     m.insert(Key(1), "one");
  |       ^^^^^^ ------ `Key` doesn't satisfy the trait bound
  |       |
  |       the trait `Hash` is not implemented for `Key`
  |
note: required by a bound in `HashMap::<K, V, S, A>::insert`
help: consider annotating `Key` with `#[derive(Hash)]`
  |
4 + #[derive(Hash)]
5 | struct Key(u32);

The type already derived PartialEq and Eq and missed Hash. The help line gives the exact line to add. This is the typical case for a derive: when the bound is a standard trait that has a mechanical implementation (Clone, Copy, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord), the fix is to add it to #[derive(...)]. Remember that Hash and Eq must agree for a map key: two equal keys must hash the same, which derives guarantee when you derive both.

Form four: the wrong kind of index

Indexing a vector with an i32 is a type error:

Text
error[E0277]: the type `[{integer}]` cannot be indexed by `i32`
 --> d277.rs:4:27
  |
4 |     println!("{}", scores[i]);
  |                           ^ slice indices are of type `usize` or ranges of `usize`
  |
  = help: the trait `SliceIndex<[{integer}]>` is not implemented for `i32`

Indices are usize, because that is the size of a pointer, and Rust does not convert integer types silently. The fix is to convert at the point of use (scores[i as usize]), or better, to keep the index as a usize from the start. If i might be negative, as usize wraps it to a huge number and the access panics; use usize::try_from(i) and handle the error when negative values are possible.

Form five: moving a non-Send type to another thread

The thread version of this error is the longest and the most useful:

Text
error[E0277]: `Rc<Vec<i32>>` cannot be sent between threads safely
 --> e277.rs:6:32
  |
6 |       let handle = thread::spawn(move || {
  |                    ------------- ^------
  |                    |             |
  |  __________________|_____________within this `{[email protected]:6:32: 6:39}`
  | |                  required by a bound introduced by this call
7 | |         println!("{:?}", data);
8 | |     });
  | |_____^ `Rc<Vec<i32>>` cannot be sent between threads safely
  |
  = help: within `{[email protected]:6:32: 6:39}`, the trait `Send` is not implemented for `Rc<Vec<i32>>`
note: required because it's used within this closure
note: required by a bound in `spawn`

thread::spawn requires its closure to be Send, and the closure captures an Rc. An Rc counts its owners with a non-atomic counter, so two threads touching it could corrupt the count. The standard library marks it not Send, and the compiler enforces that statically. The repair is the atomic sibling, Arc:

src/main.rsrust
use std::sync::Arc;
use std::thread;

fn main() {
    let data = Arc::new(vec![1, 2, 3]);
    let handle = thread::spawn({
        let data = Arc::clone(&data);
        move || println!("{:?}", data)
    });
    handle.join().unwrap();
}
Text
[1, 2, 3]

A data race that would be a heisenbug in other languages is a compile error here. For Send and Sync in full, see Send and Sync explained.

Choosing the repair

The missing traitThe usual repair
FromResidual (the ? operator)Make the function return Result or Option
DisplayImplement it by hand, or print with {:?} after deriving Debug
Hash, Eq, Clone, Copy, Default, OrdAdd to #[derive(...)]
SliceIndexUse a usize index
Send or SyncReplace Rc with Arc, RefCell with Mutex, or keep the work on one thread
A trait you definedWrite the impl block
A trait from another crate on a type from another crateYou cannot; wrap the type in a newtype (the orphan rule)

The table is a lookup, not a rule book. Each row came from the five forms above, and your case may need two of them at once; the compiler reports every unsatisfied bound in one pass, so a type that is missing both Hash and Clone produces two errors, and one edit to the derive list clears both. When two errors seem to contradict each other, fix the first and rebuild, because the second is often a consequence of the first.

When the error points at a bound you did not write

Sometimes the required by a bound in ... note names a function you called, deep in a library, and the trait it asks for seems unrelated to your code. Follow the chain: the bound belongs to that function's signature, the type is the one you passed to it, and the fix is on your type. Use the help text, then the documentation of the function, and if both are unclear, write the smallest program that reproduces the error. The sibling errors are E0599, no method found and E0308, mismatched types, which appear when the problem is a missing method or a different type and not a missing trait.

Taggede0277
Share

Keep reading

Get new posts by email

We send one email when a new post is published. No spam.

Unsubscribe in one click. No spam, ever.