In Ruby, why is `for x in list` discouraged in favour of `list.each`, and what does the for loop leave behind?
answer
- no new variable scope
- loop variable outlives the loop
- one shared variable for every closure
- for calls each under the hood
- RuboCop Style/For defaults to each
basics
~20 sRuby's for...in calls each on the collection but opens no new scope, so its loop variable and body locals survive the loop and every closure made inside shares one variable; each gives every iteration a fresh block parameter.
solid answer
~40 s`for item in list` is syntax that calls `list.each`, but unlike a block it does not create a new variable scope. The loop variable is an ordinary local of the surrounding method: after the loop it still holds the last element, or `nil` if the collection was empty, and any local first assigned in the body survives too. Lambdas created in the body all close over that one variable, so `for i in 1..3; procs << -> { i }; end` makes every proc return `3`, while `(1..3).each { |i| procs << -> { i } }` gives each iteration its own `i`. A `for` loop returns the collection, the same as `each`. Idiomatic Ruby writes `each` (or a more specific Enumerable method), and RuboCop's `Style/For` cop enforces `each` by default.
code
ruby · 11 linesfor zone in [:north, :south]
last_seen = zone
end
p zone # => :south
p last_seen # => :south
[:east, :west].each do |side|
first_seen = side
end
p defined?(side) # => nil
p defined?(first_seen) # => nilgo deeper
Recall that for calls each, returns the collection, and leaves its loop variable defined after the loop; say that idiomatic Ruby writes each.
Explain that the difference is scope: blocks open one and for does not, which is why lambdas made in a for body all see the final value.
Point to real bugs the leak causes, such as callbacks bound to the last element or a temporary shadowing a later name, and why the Style/For autocorrect is unsafe.
Frame for as a readability and consistency decision: one iteration idiom across a codebase lets reviewers and tooling reason about scope without special cases.
## What `for...in` actually is Ruby has a `for` loop that looks familiar to people arriving from other languages: ```ruby for sensor in sensors puts sensor.name end ``` It is not a separate iteration engine. The language reference describes it as `for`, a variable, `in`, and "the value to iterate over using `#each`". In other words, `for` asks the collection for its `each` method and runs the body once per yielded value. Anything that responds to `each` works: arrays, hashes, ranges, or your own class. An object without `each` fails with a `NoMethodError` for `each`. So the question is never "is `for` faster or more powerful than `each`?" Both end up in the same `each` call. The difference is **scope**. ## The scope difference A block (`do ... end` or `{ ... }`) opens a new local-variable scope. Its parameters, and any variable first assigned inside it, exist only inside that block. A `for` loop opens **no** scope: the loop variable and the body's locals belong to the enclosing method or script. | Aspect | `for x in list` | `list.each { \|x\| ... }` | |---|---|---| | New variable scope | no | yes | | `x` after the loop | last element (or `nil` when the list was empty) | undefined unless it existed before | | Locals first set in the body | still visible after the loop | gone after the block | | Closures made in the body | share one `x` | each iteration has its own `x` | | Return value | the collection | the collection | | Destructuring | `for k, v in hash` | `hash.each { \|k, v\| ... }` | Two consequences follow: - **The variable leaks.** After `for x in [1, 2, 3]; end`, `x` is `3`. After a loop over an empty array it is `nil`: the parser has seen the assignment, so the local exists, but nothing was ever assigned to it. - **Body locals leak.** A temporary such as `reading = sensor.read` inside a `for` body is still readable after the loop and can silently shadow a later use of the same name. ## The closure trap The leak matters most when the body creates a closure. Consider a greenhouse controller building one callback per sensor slot: ```ruby callbacks = [] for slot in 1..3 callbacks << -> { "slot #{slot}" } end callbacks.map(&:call) # => ["slot 3", "slot 3", "slot 3"] callbacks = [] (1..3).each { |slot| callbacks << -> { "slot #{slot}" } } callbacks.map(&:call) # => ["slot 1", "slot 2", "slot 3"] ``` With `for` there is exactly one `slot` variable, reassigned on every pass, and each lambda captures that variable rather than a snapshot of its value. By the time the lambdas run, it holds the last value. With `each`, every call of the block gets a fresh `slot` parameter, so each lambda keeps its own. ## What stays the same It is worth being precise, because weak answers overstate the difference: 1. Both forms call `each` on the collection, so both need an object that responds to `each`. 2. Both return the collection itself when they run to completion. 3. Both can be left early with the jump keywords, which have their own rules. 4. Both can destructure pairs: `for key, value in hash` works just as `hash.each { |key, value| ... }` does. The difference is only where the variables live. ## Why idiomatic Ruby prefers `each` - **No leaked state.** Variables stay inside the block, which keeps methods short-lived and avoids accidental reuse of a name. - **Closures behave as readers expect.** Each iteration captures its own values. - **One style for everything.** Once you write `each`, switching to `map`, `select`, `each_with_index` or `each_slice` is a one-word change; a `for` loop has to be rewritten. - **Tooling agrees.** RuboCop's `Style/For` cop has `EnforcedStyle: each` in its default configuration, and its autocorrection is marked unsafe precisely because turning `for` into `each` changes variable scope. - **Readers expect it.** The language reference itself notes that `for` is rarely used in modern Ruby programs. The practical interview answer: `for` works, but it is `each` with the scope removed, and the removed scope is a source of bugs rather than a feature.
- What does the loop variable hold after a Ruby for loop over an empty array?It holds `nil`. The parser has already seen the assignment in `for x in []`, so `x` exists as a local of the enclosing scope, but the body never ran and nothing was assigned to it. `defined?(x)` returns `"local-variable"`, not `nil`.
- Does a Ruby for loop work on an object that has no each method?No. `for` iterates by calling `each` on the object after `in`, so a class without `each` raises `NoMethodError` for `each`. Defining `each` on your own class is enough to make it usable in a `for` loop and in a block-based `each` call.
- If for and each both call each, why does RuboCop mark its Style/For autocorrection unsafe?Because the rewrite changes scope. Code after a `for` loop may read the loop variable or a body local that leaked out; after the same body becomes an `each` block, those variables no longer exist there, so the corrected program can raise `NameError` or read a different value.
saying these in an interview costs you the question
- for creates its own scope, just like a block does
- The loop variable is undefined once a for loop ends
- Each closure created in a for loop captures its own copy of the variable
- for iterates by index, so it works on objects without an each method
- A for loop returns nil, unlike each which returns the receiver