# 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.