Rust's ownership system rests on three simple rules that govern every value in the language: every value has exactly one owner, there can only be a single owner at any given moment, and the value is dropped when that owner goes out of scope. The "drop" here is not a casual phrase — it means the standard library's drop function from the Drop trait is called automatically at the closing brace of the variable's scope, freeing any resources the value owns. This pattern, sometimes called RAII (Resource Acquisition Is Initialization), ties resource lifetimes to object lifetimes so that cleanup is guaranteed by the structure of the program rather than by the discipline of the programmer.
The distinction between the stack and the heap is essential to understanding why these rules matter. The stack is a last-in, first-out region of memory for values whose size is known at compile time — integers, booleans, characters, and tuples of such primitives live there directly. The heap is used for values whose size or lifetime is dynamic, and accessing them is done through pointers stored on the stack. A String, for example, has a small struct (a pointer, length, and capacity) sitting on the stack, while the actual UTF-8 bytes it owns are stored on the heap. When the String goes out of scope, the heap buffer is freed at the same time as the stack-resident struct, preventing the leaks and double-frees that plague languages with manual memory management.
Scope in Rust is delimited by curly braces, and variables are dropped at the closing brace that introduced them. Even constructs like match arms create fresh scopes, so bindings introduced inside an arm are dropped when that arm completes. Shadowing, declared with a second let using the same name, does not move or drop the previous binding — it simply rebinds the name to a new value, and crucially, the new binding may have a different type. This is distinct from mutation with let mut, which keeps the same binding and type in place but changes the underlying value. Shadowing can therefore change a variable's type across declarations, while mutation cannot.