# 2.4 Local scope and blocks
> Where Rust variables live and die: scope, blocks, shadowing across functions, and defining variables near first use.
Source: https://learnrust.net/chapter-2/local-scope-and-blocks/
Functions gave your programs rooms. This lesson is about which names are visible from which room, a concept called scope, and it answers a question you may already have wondered about: what happens when two functions both want a variable named `x`?
## Scope
A variable's **scope** is the region of code where its name can be used, and the rule in Rust is satisfyingly mechanical: a variable's scope runs *from its `let` to the closing brace `}` of the block it was declared in*. Before the `let`, the name doesn't exist yet. After the brace, it's gone.
```rust
fn main() {
let outer = 1;
{
let inner = 2;
println!("{outer} {inner}");
}
println!("{outer}");
}
```
```
1 2
1
```
The inner block can see `outer` (it's still before `outer`'s closing brace), so both print. But flip it around and try to use `inner` after its block has closed:
```rust
fn main() {
{
let inner = 2;
}
println!("{inner}");
}
```
```
error[E0425]: cannot find value `inner` in this scope
--> src/main.rs:5:16
|
5 | println!("{inner}");
| ^^^^^
|
help: the binding `inner` is available in a different scope in the same function
--> src/main.rs:3:13
|
3 | let inner = 2;
| ^^^^^
```
(Note the compiler even shows you where the binding *does* exist, in case the brace placement was the surprise.)
As far as line 5 is concerned, `inner` never existed. Scope is enforced at compile time; the compiler isn't checking whether the value is *gone*, it's checking whether the name is *visible from here*. (What happens to the value itself when its scope ends is a deeper story with its own chapter; lesson 8.3 introduces the word "dropped" properly. For the integers we're using, nothing interesting happens; the box is just reclaimed.)
A variable that lives inside a function (including the parameters) is a **local variable**, local to that function. Which sets up the answer to the opening question.
## Functions don't share variables
Each function's variables are its own. The same name in two functions means two completely unrelated variables:
```rust
fn main() {
let x = 1;
let y = 2;
println!("main: {x} {y}");
swap_demo(x);
println!("main again: {x} {y}");
}
fn swap_demo(y: i32) {
let x = 100;
println!("swap_demo: {x} {y}");
}
```
```
main: 1 2
swap_demo: 100 1
main again: 1 2
```
Read the middle line carefully: inside `swap_demo`, `x` is its own local 100, and `y` is its *parameter*, which received a copy of main's `x` (so, 1). None of it touches `main`'s variables, as the last line proves. The names match by coincidence; the variables never met.
{% callout(kind="note", title="Key insight") %}
This isolation is a feature you'll lean on constantly: when writing a function, you can name things whatever reads best *inside that function*, without checking what every other function named anything. Functions are sealed rooms, and the only doors are parameters in and return values out. (Chapters 8 and 9 are entirely about what passes through those doors; the doors themselves never change.)
{% end %}
## Where to define your variables
Since a variable exists from its `let` to its block's end, you choose its scope by choosing where to write the `let`. Old C tradition declared everything at the top of the function; modern practice (and Rust idiom) says otherwise:
{% callout(kind="best", title="Best practice") %}
Define each variable as close to its first use as you can. A variable born three lines before it's needed is three lines of "wait, what's this for?"; a variable born at first use explains itself. Bonus: shorter scopes mean fewer live names to track at any point in the function, for you and for the compiler's more advanced checks alike.
{% end %}
And the 1.11 connection completes itself: blocks are expressions, so a block can both *scope* some scratch work and *produce* its result, leaving no temporary names behind:
```rust
fn main() {
let minutes = 200;
let formatted = {
let h = minutes / 60;
let m = minutes % 60;
format!("{h}h {m}m")
};
println!("{formatted}");
}
```
```
3h 20m
```
`h` and `m` exist exactly as long as they're useful and not a line longer. (`format!` is `println!`'s sibling that produces the text instead of printing it; lesson 5.5 covers it. And `%` is the remainder operator, formally lesson 6.2.)
## Quiz time
**Question #1**
What does this program print? Work it out before running it; this is the chapter's central trace.
```rust
fn main() {
let a = 5;
let b = 10;
print_things(b);
println!("main: {a} {b}");
}
fn print_things(a: i32) {
let b = a + 1;
println!("print_things: {a} {b}");
}
```
Show solution
```
print_things: 10 11
main: 5 10
```
`print_things`'s parameter `a` receives a copy of main's `b` (10), and its local `b` is 11. Main's `a` and `b` are different variables that never changed.
**Question #2**
Predict the compiler error:
```rust
fn main() {
let total = {
let price = 40;
price * 2
};
println!("{total} from price {price}");
}
```
Show solution
E0425: cannot find value `price` in this scope, at the final `println!`. `price` died at the inner block's closing brace; only `total` (80) survived. Printing just `{total}` fixes it.
**Question #3**
True or false: a function can read its caller's local variables if it knows their names.
Show solution
False, and it's one of the load-bearing falsehoods of the whole language. A function sees its own locals and parameters, nothing else. If a function needs a caller's value, the caller passes it as an argument.
You now know how functions work mechanically. The next two lessons are about using them *well*: first why they're worth the ceremony, then how to design a program as a team of them.