Step 1 of 5 · Reading · ~4 min
Learn
Functions
Errors: error, assert, pcall
So far every function in this course has assumed its input was fine. Real ones cannot. Lua gives you three tools, and the whole design rests on one idea: an error stops the current call chain, and only a caller who explicitly asked to catch it will see it.
Raising one: error
local function checked_sqrt(x)
if x < 0 then
error("negative input", 0)
end
return math.sqrt(x)
end
print(pcall(checked_sqrt, 16))
print(pcall(checked_sqrt, -4))
--> true 4.0
--> false negative input
That trailing 0 matters more than it looks. By default error prefixes the
message with the source position — script.lua:12: negative input. Useful while
debugging; ruinous when a test compares your output byte for byte, because the
line number changes whenever you move the call. Level 0 says "no prefix".
Catching one: pcall
pcall — protected call — runs a function and converts a crash into a return
value. It returns true plus whatever the function returned, or false plus the
error:
local fine, value = pcall(function() return 1 + 1 end)
print(fine, value)
local failed, why = pcall(function() error("boom", 0) end)
print(failed, why)
--> true 2
--> false boom
Everything after the first value is the function's own return values, so
pcall(f) never needs a wrapper for the success case.
Errors you did not raise
pcall catches Lua's own runtime errors too, which is where you meet the two
messages you will see most often in this language:
print(pcall(function() return nil + 1 end))
print(pcall(function() local t = nil; return t.x end))
--> false script.lua:1: attempt to perform arithmetic on a nil value
--> false script.lua:2: attempt to index a nil value (local 't')
"Attempt to index a nil value" is the single most common Lua error in existence. It means you reached into something that was not there — usually a table field that was never set, or a function that returned nothing.
assert, the one-liner
assert(value, message) throws when the value is false or nil, and otherwise
hands the value straight back — so it is safe to wrap around an expression:
print(assert(7, "unused"))
print(pcall(function() assert(false, "assert says no") end))
--> 7 unused
--> false script.lua:2: assert says no
Note two things: assert returns all its arguments on success, and its failure
message gets the same position prefix error adds. With no message at all, Lua
supplies assertion failed!.
Often you do not need any of this
tonumber already tells you about bad input without throwing:
print(tonumber("42"), tonumber("4x"))
--> 42 nil
Prefer the nil-returning function when failure is ordinary. Save error for
"this must never happen", and pcall for the boundary where you finally handle it.
Common mistakes
- Forgetting level
0— the position prefix makes exact-output comparisons depend on your line numbers. - Expecting
pcall's second value to be the result on failure — on failure it is the error; check the first value before you use anything else. - Wrapping everything in
pcall— an error you catch and ignore is worse than a crash, because the program keeps going in a state you never reasoned about. - Using
errorfor expected input problems —tonumberreturning nil is not exceptional; it is Tuesday. - Passing arguments to the wrong place —
pcall(checked_sqrt, 16)passes the argument through;pcall(checked_sqrt(16))callschecked_sqrtbefore the protection exists.
Your exercise
Guarded Divide reads a count, then that many lines. Each line should hold two numbers. For each line it reports either the quotient or the reason there is none.
You write the two checks, each raising its own error with error(msg, 0) so the text is exact, and the pcall wrapper that turns a raised error into a printed line. Parse with tonumber and check for nil yourself: do not let Lua's own message reach the output, because its position prefix would depend on where you put the call.
The mistakes the grader will catch: forgetting the 0 (the message arrives as script.lua:7: ...), and putting the failing call outside the pcall, which aborts the whole run on the first bad line instead of reporting it and moving on to the next.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…