Where the variable went
You're writing your first non-trivial Rust function. You declare a counter inside an if block, do some work, and try to print it after the block ends. The compiler stops you cold:
error[E0425]: cannot find value `count` in this scope
Or maybe you typed Println! with a capital P somewhere, or referenced total when you actually named it Total. Same error number, different cause. E0425 is one of the most common errors a new Rust programmer sees, and it has a very specific meaning: the compiler is reading your code, hits a name, and has no idea what you're talking about. Either the name doesn't exist, it's spelled differently, or it does exist but lives somewhere your current line can't reach.
What "scope" actually means
In Rust, every variable lives inside a region of code called a scope. The scope is the area where the name is valid. When the scope ends, the name disappears. Think of scopes like rooms in a house: anything you put down in the kitchen exists in the kitchen, but the moment you walk into the living room and close the door behind you, you can't grab the kitchen knife anymore. You'd have to either go back to the kitchen, or have brought the knife with you.
Curly braces { } define a scope. Function bodies are scopes. So are if blocks, for loops, match arms, and any plain { ... } you write yourself. A variable declared inside a scope is alive from the line where it's declared until the closing brace of that same scope.
A minimal example of the error
Here's a tiny program that triggers E0425:
fn main() {
// We try to use `value` before it's declared.
println!("The value is: {value}");
// Declaration happens AFTER the use. Too late.
let value = 5;
}
Compile that and you get:
error[E0425]: cannot find value `value` in this scope
--> src/main.rs:3:30
|
3 | println!("The value is: {value}");
| ^^^^^ not found in this scope
Rust reads top-to-bottom. When it gets to line 3, the name value simply hasn't been bound yet. Move the let above the println! and the error vanishes.
The four classic causes
E0425 almost always boils down to one of four things.
Typos. The most common cause, and the most embarrassing. You wrote let username = ... and then later referenced usename. Rust is case-sensitive, so Total and total are different names. The compiler usually offers a helpful suggestion:
help: a local variable with a similar name exists: `username`
If you see that hint, take it. Auto-correct does not save you here. Fix the spelling.
Out-of-scope variables. You declared the variable inside a block, then tried to use it after the block ended:
fn main() {
let condition = true;
if condition {
// `result` only exists between these braces.
let result = 42;
}
// Outside the if-block, `result` is gone. E0425.
println!("Result was {result}");
}
The fix is to declare the variable outside the block and assign to it inside. Rust has a neat shortcut for this: an if is an expression, so you can let it produce the value directly.
fn main() {
let condition = true;
// The whole if/else evaluates to a value. Both arms must have the same type.
let result = if condition { 42 } else { 0 };
println!("Result was {result}");
}
Forgetting let. In Python or JavaScript you can write count = 0 and a new variable appears. Rust doesn't work that way. Without let, you're not declaring anything, you're trying to assign to something that already exists. If count was never declared, you get E0425.
fn main() {
count = 0; // No `let`. Rust looks for an existing `count` and finds none.
count += 1;
println!("{count}");
}
Missing use for items from other modules. If MyStruct is defined in a sibling module, you can't just write MyStruct::new() without importing it or qualifying the path. Strictly speaking, this often manifests as E0433 ("failed to resolve") for paths and E0425 for bare names, but the underlying confusion is the same: the compiler doesn't know where to look.
A more realistic example: a parser sketch
Here's the kind of code where this error sneaks up on you in real projects.
fn parse_number(input: &str) -> Result<i64, String> {
// Try to parse the string. The result is a Result, not the number itself.
let parsed = input.trim().parse::<i64>();
// We try to use `value` here, but `value` is never declared.
// The actual variable is `parsed`. Classic typo-induced E0425.
match value {
Ok(n) => Ok(n),
Err(e) => Err(format!("not a number: {e}")),
}
}
The compiler tells you exactly which line and offers parsed as a suggested fix. Read the error message. Rust's diagnostics are unusually good. Don't ignore the help text underneath the red squiggle.
A subtler case: a variable that exists in one branch of a match but not another.
fn describe(n: i32) -> String {
match n {
0 => {
// `label` only exists in this arm.
let label = "zero";
label.to_string()
}
_ => {
// Trying to read `label` here is E0425. It was never bound in this arm.
format!("a number called {label}")
}
}
}
Each match arm is its own little scope. A binding in one arm has nothing to do with another arm.
Reading the compiler error
Rust's error output is structured. Train yourself to look at it carefully.
error[E0425]: cannot find value `value` in this scope
--> src/main.rs:3:30
|
3 | println!("The value is: {value}");
| ^^^^^ not found in this scope
|
help: a local variable with a similar name exists: `valuee`
Line 1: the error code and a one-line description. The error code (E0425) is searchable. Run rustc --explain E0425 from your terminal for a longer explanation.
Line 2: the file and line:column where the offending name appears.
The caret line: which characters in the source are the problem.
The help: line: the compiler is guessing at fixes. These guesses are usually right. If the suggestion looks like what you meant, take it.
Fixes, in order of how often they apply
Check the spelling first. Compare the name letter for letter with the original declaration.
If the spelling is right, look at the closing brace above the line that errored. Did the variable's scope end? If yes, restructure so the binding lives in the outer scope or use an expression that returns a value.
If you're assigning, did you forget let?
If the name belongs to another module, add a use statement at the top of the file. For example, use std::collections::HashMap; brings HashMap into scope.
If the name belongs to a different match arm or if let branch, accept that and either move the logic or factor the binding out.
Related compiler errors that look similar
E0425 is about names that don't exist. A few cousins:
E0433 fires when a path fails to resolve, like crate::foo::Bar when Bar isn't there. Often a missing use or a wrong module path.
E0412 is "cannot find type." Same kind of issue but for type names instead of values.
E0382 is "value used after being moved." That's a totally different problem: the binding exists, but its data moved elsewhere.
If your error says "borrow of moved value" or "use of moved value," you're not in E0425 territory.
Where to go next
Scope and naming are a doorway into how Rust thinks about ownership and lifetimes. Once you stop tripping over E0425, the next layer of errors gets more interesting.
How to fix Rust E0382 value used after being moved