10. Error handling
Like many other languages, Mojo has built-in support for error handling. In Mojo, an error is raised using the keyword raise
and handled using except
. Any function call that can potentially raise an error must be wrapped within a try
and except
block.
The following diagram illustrates the structure of error handling in Mojo.
The following example shows how a function declares that it raises an error and how the caller of a function handles it.
fn raise_error(cond: Bool) raises:
if cond:
raise Error("Provided condition is True")
else:
print("No error")
Usage:
try:
raise_error(True)
except e:
print("Error raised:", e)
In the example, an error is raised inside the function raise_error
based on a given condition. Since an error is raised, the function must declare that in its signature. This means that any one calling the function must either handle the error raised by the function, or must re-raise it further down. In the usage example, we see that the error raised by the function is handled.
It is possible to raise a String
as error as shown below, though in reality it automatically gets wrapped within an Error
.
fn raise_str_error(cond: Bool) raises:
if cond:
raise "String error is allowed"
else:
print("No error")
Usage:
try:
raise_str_error(True)
except e:
print("Error raised:", e)
10.1. Error propagation
In the following example, we see that any function that calls another function that potentially raises an error, needs to either fully handle the error within the function itself, or declare in its own signature that it too raises an error. Unhandled errors in a called function will get propagated down.
fn raise_call() raises: # Need to either have 'raises' in the signature, or wrap with try-except
raise_error(True)
10.2. Finally
The finally
code blocks always gets executed regardless whether an error is getting propagated, or it was fully handled. The finally
block is typically used for clean-up activities, for example, if a file is opened within a try
block, then we must close the file within a finally
block to ensure that even if an error is raised within the try
block the file is always closed before the function returns. If a value is returned in finally
and try
or except
, the returned value will be from finally
. The finally
block is optional.
try:
raise_error(True)
except e:
print("Error raised:", e)
finally:
print("Always executed")
try:
raise_error(False)
except e:
print("Error raised:", e)
finally:
print("Always executed")
10.3. Else
In order to execute statements when no error has been raised, you can use else
block. The else
block is optional. The else
block appears after except
, but before finally
. The else
block is useful for those cases where we want to isolate error raising functions that we want to handle from other code that we want to execute, but may raise their own errors.
try:
raise_error(False)
except e:
print("Error raised:", e)
else:
print("No exception raised.")