Error Handling
The result utility can be used for more robust error handling.
Errors are represented as values instead of being thrown using error(), this makes it clear that a function can fail, so you don't forget to handle a potential error which may lead to unexpected behavior or crashes.
Creating results
A result can be in two states: Ok or Err. The Ok state represents a successful value, and the Err state represents an error value.
To create an Ok result, we can use Result.Ok:
local res = Result.Ok(5)To create an Err result, we can use Result.Err:
local res = Result.Err("something went wrong")You can return a result from a function to indicate that the function can fail:
function divide(a: number, b: number): Result.Result<number, string>
if b == 0 then
return Result.Err("cannot divide by zero")
end
return Result.Ok(a / b)
endIf you need to wrap a function that throws errors with a result, you can use Result.try:
local res = Result.try(function()
error("something went wrong")
end)Any errors thrown within the function will be caught and returned as an Err, otherwise the return value will be wrapped in an Ok.
Accessing the inner value
There are multiple ways to access the inner value of a result.
The easiest way is to use Result.unwrap:
local res = Result.Ok(5)
print(Result.unwrap(res)) -- 5
local res = Result.Err("something went wrong")
print(Result.unwrap(res)) -- throws "something went wrong"If the result is an Err, Result.unwrap will throw an error.
Most of the time, we want to handle the error case without crashing the program. There are other methods to access the inner value of a result without throwing an error.
You can use Result.unwrapOr to get the Ok value or a default value in case of an Err:
local res = Result.Ok(5)
print(Result.unwrapOr(res, 0)) -- 5
local res = Result.Err("something went wrong")
print(Result.unwrapOr(res, 0)) -- 0The default value is eagerly evaluated, so it's always evaluated, even if the result is an Ok.
You can use Result.unwrapOrElse to get the Ok value or compute a default value in case of an Err:
local res = Result.Ok(5)
print(Result.unwrapOrElse(res, function()
return 0
end)) -- 5However, there are some cases where you don't want to throw an error or use a default value, but instead want to perform different actions based on whether the result is Ok or Err. You can use the match method for this:
local res = Result.Ok(5)
Result.match(res, {
Ok = function(value)
print("success:", value)
end,
Err = function(err)
print("error:", err)
end
})You can also use the unpack method, which returns two values: the Ok value or nil, and the Err value or nil:
local res = Result.Ok(5)
local ok, err = Result.unpack(res)
print(ok, err) -- 5 nilThis is useful to propagate the error in the function, for example, using the divide function from before:
local function doCalculation(x: number): Result<number, string>
local res = divide(x, x * x)
local ok, err = res:unpack()
if err then
return Result.Err(err)
end
return ok
endThis function will return the result of divide if it's Ok, otherwise it will return the error. This is the recommended way to propagate errors.
Transforming results
You can use the map method to transform the inner value of a result:
local res = Result.Ok(5)
local doubled = res:map(function(value)
return value * 2
end)This will double the value inside the Ok result, or preserve the Err value.
You can also map the error instead using the mapErr method:
local some = Result.Err(5)
local doubled = some:mapErr(function(value)
return value * 2
end)You can also use the andThen method to transform the inner value of a result and return a new result:
local res = Result.Ok(5)
local doubled = res:andThen(function(value)
if value % 2 == 0 then
return Result.Ok(value * 2)
else
return Result.Err("value is not even")
end
end)This will double the value inside the Ok result if it's even, or return the Err value if it's odd. If the result is an Err, the andThen method will preserve the Err value.
You can also use the match method from earlier by returning a value from the callbacks:
local res = Result.Ok(5)
local doubled = Result.match(res, {
Ok = function(value)
return value * 2
end,
Err = function(err)
return 0
end
})Learn more
There are more methods available on the Result type, this was just a small tour of what you can do with results. If you want to learn more, see the reference for more information.