Before this lesson, complete lua-modules-errors, lua-control-functions.
This course runs standard Lua 5.5 with its standard library. Game engines, LuaJIT and embedded host APIs are outside this sequence.
Open files have a lifetime separate from the string or table values you compute from them. io.open returns a handle on success and a failure result when opening is impossible. Check the handle before reading. In this lab, an open failure is an expected API result, so normalize it to nil and the stable message open failed rather than returning an operating-system-dependent diagnostic.
Apply the contract
A file handle's lines iterator reads records without their line terminators. An empty string represents an empty line and is truthy in Lua; nil ends iteration. A final unterminated line is still a line. Do not filter with a nonempty-string condition unless the contract explicitly asks to discard blank records. Preserve spaces in each record and leave the original file unchanged.
local function example()
local file <close> = assert(io.tmpfile())
assert(file:write("first\n\nlast"))
assert(file:seek("set",0))
for line in file:lines() do io.write("[",line,"]\n") end
end
example()Reason about the edge cases
A to-be-closed local, written local file <close> = ..., closes its resource when its scope exits, including ordinary returns and error unwinding. This is available in the standard Lua version used here; it is not a LuaJIT portability promise. Explicit close with correct error-path handling can also satisfy the lab. Garbage collection is not a deterministic substitute for the requirement that the handle be closed before return. The driver observes the actual handle after the call, so returning the right lines while leaving an open resource still fails. Use io.tmpfile in the runnable example to practice lifetime behavior without relying on host files.
Practice
Implement M.read_lines(path). On a successful open, return a dense sequence of lines with line terminators removed, preserving empty lines and spaces; a final unterminated line is still included. Close the handle before returning and leave file contents unchanged. If opening fails return nil and open failed. Use Lua 5.5 standard I/O; explicit close or a to-be-closed local are both valid.
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.