E0382
error[E0382]: use of moved value: `s1`
When you assign a heap-allocated value (like String or Vec) to another variable, ownership moves to the new variable. The original is gone — you can't use it afterwards. This is Rust's ownership system eliminating use-after-free bugs at compile time.
❌ Broken
let s1 = String::from("hello");
let s2 = s1; // s1 moves into s2
println!("{}", s1); // ❌ E0382: s1 was moved✅ Fixed
let s1 = String::from("hello");
let s2 = s1.clone(); // deep copy — both stay valid
println!("{} {}", s1, s2); // ✅
// Or borrow instead of moving:
let s2 = &s1;
println!("{} {}", s1, s2); // ✅💡 Tip: Primitive types (i32, bool, char, f64) implement Copy and are duplicated automatically. Heap types (String, Vec, Box) move.
E0502
error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
Rust allows many shared (&) references OR one exclusive (&mut) — never both at the same time. If you have an active immutable borrow, you cannot take a mutable borrow until the immutable one ends. This rule eliminates data races by construction.
❌ Broken
let mut v = vec![1, 2, 3];
let first = &v[0]; // immutable borrow
v.push(4); // ❌ E0502: mutable borrow while immutable exists
println!("{}", first);✅ Fixed
let mut v = vec![1, 2, 3];
let first = v[0]; // copy the value out (i32 is Copy)
v.push(4); // ✅ no active borrow
println!("{}", first);
// Or restructure scope:
let mut v = vec![1, 2, 3];
{
let first = &v[0];
println!("{}", first);
} // first's borrow ends here
v.push(4); // ✅💡 Tip: The borrow checker is conservative. If it's fighting you, try moving the println earlier, copying the value instead of borrowing, or using .clone().
E0499
error[E0499]: cannot borrow `v` as mutable more than once at a time
Only one mutable reference (&mut) can exist at a time. This prevents data races in single-threaded code and is the fundamental rule that makes Rust's concurrency guarantees work.
❌ Broken
let mut v = vec![1, 2, 3];
let a = &mut v;
let b = &mut v; // ❌ E0499: second mutable borrow
a.push(4);
b.push(5);
✅ Fixed
let mut v = vec![1, 2, 3];
{
let a = &mut v;
a.push(4);
} // a's borrow ends here
let b = &mut v; // ✅ now safe
b.push(5);💡 Tip: Use scopes {} to limit how long a mutable borrow lives. In practice this error often means restructuring your logic so mutations happen sequentially.
E0308
error[E0308]: mismatched types
expected `i32`, found `&i32`
The type the compiler inferred or expects doesn't match what you provided. Very common when iterating (iterators yield references), when a function returns () instead of a value, or when mixing signed/unsigned integers.
❌ Broken
// Trailing semicolon returns () instead of i32:
fn double(n: i32) -> i32 {
n * 2; // ❌ E0308: returns () not i32
}
// Iterator reference confusion:
let nums = vec![1, 2, 3];
let sum: i32 = nums.iter().sum(); // needs type annotation✅ Fixed
fn double(n: i32) -> i32 {
n * 2 // ✅ no semicolon — expression returned
}
// Dereference in closures when iterating:
let nums = vec![1, 2, 3];
let doubled: Vec<i32> = nums.iter().map(|&x| x * 2).collect();
// ^ dereference the &i32💡 Tip: A trailing semicolon on the last line of a function silently changes the return type to (). This is the #1 beginner mistake in Rust.
E0277
error[E0277]: `MyType` doesn't implement `std::fmt::Display`
The type doesn't implement a required trait. Most often triggered by trying to use {} formatting without Display, trying to compare types without PartialOrd, or calling methods that require a specific trait.
❌ Broken
struct Point { x: i32, y: i32 }
let p = Point { x: 1, y: 2 };
println!("{}", p); // ❌ E0277: Point doesn't implement Display
println!("{:?}", p); // also fails — needs Debug✅ Fixed
#[derive(Debug)]
struct Point { x: i32, y: i32 }
let p = Point { x: 1, y: 2 };
println!("{:?}", p); // ✅ Debug via #[derive(Debug)]
// Implement Display manually for custom formatting:
use std::fmt;
impl fmt::Display for Point {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "({}, {})", self.x, self.y)
}
}
println!("{}", p); // ✅💡 Tip: Add #[derive(Debug)] for {:?} printing. For {} printing, implement std::fmt::Display manually. Most missing bounds are Debug, Display, Clone, PartialEq, or PartialOrd.
E0106
error[E0106]: missing lifetime specifier
expected named lifetime parameter
When a function returns a reference and has multiple reference parameters, Rust can't infer which input the output reference comes from. You need to tell it with lifetime annotations.
❌ Broken
// Compiler doesn't know if the return borrows from x or y:
fn longest(x: &str, y: &str) -> &str { // ❌ E0106
if x.len() > y.len() { x } else { y }
}✅ Fixed
// 'a says: output lives no longer than the shorter input
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { // ✅
if x.len() > y.len() { x } else { y }
}
// Single input → no annotation needed (elision rule):
fn first_word(s: &str) -> &str { // ✅ lifetime elided
let bytes = s.as_bytes();
for (i, &b) in bytes.iter().enumerate() {
if b == b' ' { return &s[..i]; }
}
&s[..]
}💡 Tip: Lifetime annotations are constraints, not durations. 'a means 'the output can't outlive the inputs'. Most lifetimes are inferred (elided). You only need explicit ones when the compiler can't figure out the relationship.
E0596
error[E0596]: cannot borrow `x` as mutable, as it is not declared as mutable
You tried to modify a variable that wasn't declared with mut. In Rust, variables are immutable by default — the compiler enforces this so accidental mutations are caught at compile time.
❌ Broken
let x = 5;
x = 10; // ❌ E0596: x is immutable
let v = vec![1, 2, 3];
v.push(4); // ❌ E0596: v is immutable
✅ Fixed
let mut x = 5; // ✅ mut makes it mutable
x = 10;
let mut v = vec![1, 2, 3];
v.push(4); // ✅
💡 Tip: Add mut after let. Only add mut when the variable genuinely needs to change — immutability-by-default catches bugs and makes code easier to reason about.
E0384
error[E0384]: cannot assign twice to immutable variable `x`
Similar to E0596 but triggered by a second assignment rather than a method call. The variable was declared without mut but you tried to reassign it.
❌ Broken
let x = 5;
x = 10; // ❌ E0384
✅ Fixed
let mut x = 5;
x = 10; // ✅
// Shadowing is another option if you want to "replace" the variable:
let x = 5;
let x = 10; // ✅ shadowing — creates a new binding named x
💡 Tip: Shadowing (let x = ...) and mutation (mut x = ...) look similar but are different: shadowing creates a new variable (and can change the type), mutation modifies the same binding.
E0507
error[E0507]: cannot move out of `*s` which is behind a shared reference
You tried to move a value out of a reference. You can't take ownership through a &T — the reference doesn't own the data.
❌ Broken
fn print_name(s: &String) {
let owned = *s; // ❌ E0507: can't move out of &String
println!("{}", owned);
}✅ Fixed
fn print_name(s: &String) {
let owned = s.clone(); // ✅ clone the data
println!("{}", owned);
}
// Or just use the reference directly:
fn print_name(s: &String) {
println!("{}", s); // ✅ no need to own it
}💡 Tip: If you only need to read the value, use the reference directly. If you need an owned copy, call .clone(). If you need ownership, change the parameter to take T instead of &T.
E0004
error[E0004]: non-exhaustive patterns: `None` not covered
A match expression must handle every possible case. If any variant is unhandled, the code won't compile. This eliminates an entire class of runtime bugs where unhandled cases cause crashes.
❌ Broken
let opt: Option<i32> = Some(42);
match opt {
Some(n) => println!("{}", n),
// ❌ E0004: None not covered
}✅ Fixed
match opt {
Some(n) => println!("{}", n),
None => println!("nothing"), // ✅ exhaustive
}
// Use _ as a catch-all:
match opt {
Some(n) => println!("{}", n),
_ => {}, // ✅ handles everything else
}
// Or use if let for a single case:
if let Some(n) = opt {
println!("{}", n); // ✅ no else needed
}💡 Tip: The compiler tells you exactly which variants are missing. _ is a catch-all. if let is shorthand when you only care about one pattern.
E0072
error[E0072]: recursive type `List` has infinite size
A recursive type where each variant directly contains itself would require infinite memory to allocate. Rust needs to know the size of every type at compile time. The fix is indirection via Box, which has a known pointer size.
❌ Broken
enum List {
Cons(i32, List), // ❌ E0072: infinite size
Nil,
}✅ Fixed
enum List {
Cons(i32, Box<List>), // ✅ Box has fixed pointer size (8 bytes)
Nil,
}
let list = List::Cons(1, Box::new(List::Cons(2, Box::new(List::Nil))));💡 Tip: Box<T> is the simplest heap pointer. It has a fixed size (one pointer width) regardless of T, breaking the recursive size calculation.
E0716
error[E0716]: temporary value dropped while borrowed
You created a temporary value, took a reference to it, and the temporary was dropped before the reference was used. The reference would be dangling — pointing to freed memory.
❌ Broken
let s: &str = &String::from("hello")
.to_uppercase(); // ❌ E0716: temporary dropped
// Also common with chains:
let name = get_user().name; // if get_user() returns a temp✅ Fixed
// Bind the temporary to a variable first:
let upper = String::from("hello").to_uppercase();
let s: &str = &upper; // ✅ upper lives long enough
println!("{}", s);💡 Tip: If you get this error, introduce a variable to hold the intermediate value. The lifetime of a let binding extends to the end of the block.
E0515
error[E0515]: cannot return value referencing local variable `local`
You tried to return a reference to a local variable. When the function returns, the local variable is dropped, leaving a dangling reference. Rust prevents this at compile time.
❌ Broken
fn get_greeting() -> &str {
let s = String::from("hello");
&s // ❌ E0515: s is dropped when function returns
}✅ Fixed
// Return an owned value:
fn get_greeting() -> String {
String::from("hello") // ✅ caller owns it
}
// Or return a &'static str literal:
fn get_greeting() -> &'static str {
"hello" // ✅ lives for the entire program
}💡 Tip: If you need to return a reference, it must come from the function's inputs (borrowing from a parameter) or be 'static. When in doubt, return an owned type (String, Vec, etc.).
E0369
error[E0369]: binary operation `>` cannot be applied to type `T`
You used an operator on a generic type T, but T doesn't have a trait bound requiring that operator to exist. Generic code must be explicit about what operations a type supports.
❌ Broken
fn largest<T>(list: &[T]) -> &T {
let mut biggest = &list[0];
for item in list {
if item > biggest { // ❌ E0369: T doesn't implement PartialOrd
biggest = item;
}
}
biggest
}✅ Fixed
fn largest<T: PartialOrd>(list: &[T]) -> &T { // ✅ add bound
let mut biggest = &list[0];
for item in list {
if item > biggest {
biggest = item;
}
}
biggest
}💡 Tip: When you get E0369, the error message tells you which trait you need: PartialOrd for comparisons, Add for +, Display for {}, etc. Add it as a bound: <T: TraitName>.
E0428
error[E0428]: the name `connect` is defined multiple times
You defined two items (functions, structs, types, etc.) with the same name in the same scope. Rust doesn't allow this — names must be unique within a module.
❌ Broken
fn connect() -> String { "tcp".into() }
fn connect() -> String { "udp".into() } // ❌ E0428: duplicate✅ Fixed
fn connect_tcp() -> String { "tcp".into() }
fn connect_udp() -> String { "udp".into() } // ✅ unique names
// Or use a parameter:
fn connect(protocol: &str) -> String { protocol.into() }💡 Tip: This also happens with use statements importing conflicting names. Use as to rename: use std::io::Error as IoError;
E0061
error[E0061]: this function takes 2 arguments but 3 arguments were supplied
You called a function with more or fewer arguments than its signature declares. Rust does not support default parameters or variadic functions (except via macros like println!).
❌ Broken
fn add(a: i32, b: i32) -> i32 { a + b }
add(1, 2, 3); // ❌ E0061: 3 args, expected 2
add(1); // ❌ E0061: 1 arg, expected 2✅ Fixed
add(1, 2); // ✅
// For optional parameters, use Option:
fn greet(name: &str, title: Option<&str>) {
match title {
Some(t) => println!("{} {}", t, name),
None => println!("{}", name),
}
}
greet("Alice", None);
greet("Bob", Some("Dr."));💡 Tip: Rust has no default parameters. Use Option<T> for optional values, or builder structs for functions with many optional fields.
E0433
error[E0433]: failed to resolve: use of undeclared crate or module `HashMap`
You used a type or function that isn't in scope. For standard library types outside the prelude (like HashMap, BTreeMap, BufReader), you need an explicit use statement.
❌ Broken
let mut map = HashMap::new(); // ❌ E0433: HashMap not in scope
✅ Fixed
use std::collections::HashMap;
let mut map = HashMap::new(); // ✅
// Common imports:
use std::collections::{HashMap, HashSet, BTreeMap};
use std::io::{self, BufRead, Write};
use std::fmt;💡 Tip: The Rust prelude automatically imports common types (Vec, String, Option, Result, etc.). Everything else needs an explicit use. Rust Analyzer / rust-analyzer will suggest the correct use path.
error: expected `;`, found `let`
error: expected expression, found keyword `fn`
Syntax errors — the parser found something unexpected. Usually a missing semicolon, an unclosed delimiter, a typo in a keyword, or a misplaced expression.
❌ Broken
fn main() {
let x = 5 // ❌ missing semicolon
let y = 10;
fn inner() {} // ❌ nested fn needs to be at statement level
x + y // if this is a statement, not a return, add ;
}✅ Fixed
fn main() {
let x = 5; // ✅ semicolon added
let y = 10;
fn helper() {} // ✅ nested fn is valid in Rust
println!("{}", x + y);
}💡 Tip: Rust's error messages almost always point to the right line. If a semicolon error seems wrong, check the line above — a missing ; on the previous statement shifts the parser into a weird state.
error[E0275]: overflow evaluating the requirement `Box<T>: Sized`
The compiler got stuck in an infinite loop trying to prove a trait bound. Most commonly caused by a recursive trait implementation or a type that tries to implement a trait that requires itself.
❌ Broken
// Generic function calling itself with an incompatible bound:
fn process<T: Clone>(x: T) {
process(x.clone()); // infinite recursion in type inference
}✅ Fixed
// Add a base case or restructure logic:
fn process(x: String) {
if x.is_empty() { return; }
process(x[1..].to_string());
}💡 Tip: This error almost always points to a recursive type or trait bound loop. Simplify the trait constraints or add explicit type parameters.
error[E0425]: cannot find value `x` in this scope
You referenced a variable that doesn't exist in the current scope. Common causes: typo in variable name, variable declared in an inner block that has ended, or a loop variable used outside the loop.
❌ Broken
{
let x = 5;
}
println!("{}", x); // ❌ x is out of scope
for i in 0..10 { }
println!("{}", i); // ❌ i only lives inside the for loop✅ Fixed
let x = 5; // declare outside the block
{
println!("{}", x); // ✅ x is in scope
}
println!("{}", x); // ✅ still in scope
// Save the last loop value explicitly:
let mut last = 0;
for i in 0..10 { last = i; }
println!("{}", last); // ✅💡 Tip: Rust scoping is block-based. Variables only exist from where they're declared to the end of their enclosing block. Move the declaration up if you need a wider scope.