CodeRigor

FREE LESSON

Closures, captured state & iterators

Capture independent iterator state and use nil termination without confusing zero with absence.

Before this lesson, complete lua-tables, lua-modules-errors.

This course runs standard Lua 5.5 with its standard library. Game engines, LuaJIT and embedded host APIs are outside this sequence.

A generic for loop repeatedly calls an iterator using iteration state and a control value. When the first returned result is nil, the loop ends. False is not the termination sentinel. Additional returned values become additional loop variables. The language also supports a closing value for resource-owning iteration, which is separate from this lesson's simple closure-based range.

A range closure advances
start / 3
A range closure advances

Apply the contract

An iterator factory can hide its state in locals captured by a closure. For an inclusive range, initialize current to first minus step, advance once per call and return nil only after current exceeds last. Each factory call must create new locals, or two independent ranges will interfere. For this lab, endpoints and step are integers and step must be positive. An empty interval with first greater than last terminates without yielding a value.

Generic for termination
start / 3
Generic for termination
Lua
local function counter(start)
  local value=start
  return function() value=value+1; return value end
end
local a,b=counter(0),counter(0)
print(a(),a(),b())

Reason about the edge cases

Validate a zero or negative step before iteration begins; otherwise a loop can fail to make progress. Do not terminate merely because a valid returned value is false or zero. The build's output is numeric, but that general iterator rule becomes important in the next coroutine lesson. Test a one-element interval, a last value exactly on the step, a last bound between steps and two interleaved iterators. Inclusive boundaries and independent state are the core invariants. A closure is a useful small state machine whose transition should be clear from one call to the next.

Independent factories
start / 3
Independent factories

Practice

Implement M.range(first,last,step) for integer bounds and a positive integer step. Return a closure suitable for generic for; it yields first, first+step, and so on through last inclusively, then nil. Empty ranges yield nothing. Each factory call has independent state. Reject a nonpositive step with an error.

Run sample starts again from the first public fixture and grants no mastery evidence. Submit checks all fixtures, including edge cases. Read the specification before editing; the bug and fill phases share the build’s contract. Flashcards reinforce recall on a separate schedule.

Watch & read

Free lectures, talks and readings that cover this skill from another angle. All resources →

The first part introduces Lua through its language designer. The authored lesson and Lua 5.5 manual cover the exact APIs and advanced details in this lab.