Skip to content
Free public beta until December 4, 2026: every course is open, no card needed. After that, the first chapter of each course stays free and full access is a one-time $39 ($19 for up to 100 founding members chosen for helpful feedback). Your progress is kept. How the beta works

Recipe

Predict what happens to a value at each use

Use when: you're reading a short program and need to say whether it compiles, which names can still be used at a line, or where a value is dropped

Goal: for every use of every value: read, moved, copied, cloned or dropped, and the line of any E0382

Taught in Predicting moves · extends Find the line that frees each value

  1. 1

    For each let and each function parameter, write down the variable, its type, and the } that ends its scope.

    Example

    In fn main() { let a = String::from("x"); let n = 3; let b = a; println!("{b} {n}"); }: a: String, n: i32 and b: String, all ending at main's }.

    Why: an owner that still owns its value at that brace drops it there (rule 3).

  2. 2

    Mark each variable's type as Copy (numbers, bool, char, tuples and arrays of those) or not (String, Vec, anything containing one).

    Example

    n: i32 is Copy, so it is usable everywhere up to }. a and b are Strings, so they need tracking.

    • Copy — every use duplicates it; it can never be moved, so it stays usable to the end of its scope.
    • not Copy — track it through the next steps.

    Why: a Copy value has nothing to free, so duplicating it is always safe.

  3. 3

    Go top to bottom and label each use of a tracked name. A read (println!, .len(), indexing) leaves ownership where it is. A move hands ownership on and makes the name moved from: let x = name;, passing it to a parameter of an owned type, putting it in a tuple, or returning it. A clone (name.clone()) leaves the name as owner and makes a new value. Taking one non-Copy part of a tuple is a partial move of that part only.

    Example

    let b = a; is a move: from here, a is moved from and b owns the String. println!("{b} {n}") is a read of b and a read of n.

    Why: these are the only ways ownership can change hands.

  4. 4

    If a move is inside a loop, it repeats on every pass. If a move is inside one branch of an if, treat the value as possibly moved after the if. If a moved-from mut variable is assigned a new value, it owns again from that line.

    • a move in a loop body of a variable declared outside the loop — E0382 on that move, on the second pass.
    • a move in an if branch, then a use after the if — E0382 on the later use.

    Why: the compiler checks every path without running the program, so the worst path decides.

  5. 5

    Any use of a name while it is moved from is E0382, reported at that use. Otherwise the program compiles, and each value is dropped at the closing brace of whichever variable owns it last.

    Example

    Nothing uses a after let b = a;, so the example compiles. At }, b drops the String; a has nothing to drop; n frees nothing.

    • a late use only reads — "borrow of moved value".
    • a late use tries to move again — "use of moved value".

    Why: a moved-from name holds no value; reading it would read a buffer another owner controls.

Common mistakes

  • Thinking println! or .len() moves a value. They only read it.
  • Thinking a clone earlier in the program protects a later plain move. Each let x = name; is judged on its own.
  • Forgetting that a loop body runs more than once.
  • Thinking a moved-from variable is dropped at its brace. It has nothing to drop.
  • Thinking a String short enough, or a tuple that is mostly numbers, might be Copy.