Reading — step 1 of 5
Learn
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 divide(a, b)
if b == 0 then
error("division by zero", 0)
end
return a / b
end
print(pcall(divide, 10, 4))
print(pcall(divide, 1, 0))
--> true 2.5
--> false division by zero
That trailing 0 matters more than it looks. By default error prefixes the
message with the source position — script.lua:12: division by zero. 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 ok, res = pcall(function() return 1 + 1 end)
print(ok, res)
local ok2, err = pcall(function() error("boom", 0) end)
print(ok2, err)
--> 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(divide, 10, 4)passes them through;pcall(divide(10, 4))callsdividebefore the protection exists.
Your exercise
Guarded Divide reads a count, then that many lines. Each line should hold two
numbers. Print ok: <quotient to two decimals> when the division works, and
error: <message> when it does not.
Two failures to raise yourself, both with error(msg, 0) so the text is exact:
division by zero when the divisor is 0, and not two numbers: <the line>
when the line does not hold two numbers. Parse with tonumber and check for
nil — do not let Lua's own message reach the output, because its position prefix
would depend on where you put the call.
10 4 gives ok: 2.50; 5 0 gives error: division by zero; 9 x gives
error: not two numbers: 9 x.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…