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.

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 ():
use std::fs;
fn main() {
let text = fs::read_to_string("config.toml")?;
println!("{}", text.len());
}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:
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:
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) insteadThere 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:
use std::fmt;
impl fmt::Display for Point {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "({}, {})", self.x, self.y)
}
}(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:
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:
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:
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:
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();
}[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 trait | The usual repair |
|---|---|
FromResidual (the ? operator) | Make the function return Result or Option |
Display | Implement it by hand, or print with {:?} after deriving Debug |
Hash, Eq, Clone, Copy, Default, Ord | Add to #[derive(...)] |
SliceIndex | Use a usize index |
Send or Sync | Replace Rc with Arc, RefCell with Mutex, or keep the work on one thread |
| A trait you defined | Write the impl block |
| A trait from another crate on a type from another crate | You 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.


