Error Sets
Every Flint function can fail; the error is an implicit first return value of every function. No exceptions, the error path is compiled into the call/return code.
Defining & Throwing
error ErrSet:
Value1, Value2;
def fail():
throw ErrSet.Value1;
Errors are a set type; values look like enum tags.
Catching
fail() catch err:
print("Error catched!\n");
Catching consumes the error; execution continues normally after the catch.
Refinement
An error set can extend another, becoming a superset:
error ErrBase:
B1, B2;
// contains B1, B2, S1, S2
error ErrSpecial(ErrBase):
S1, S2;
anyerror is the null-set (root). Every set implicitly extends anyerror if not extending any other error, so any error can be "cast" to anyerror. The tree relation is compile-time only.
Function Signatures
def divide(i32 x, i32 y) -> i32 {ErrArithmetic}:
if y == 0:
throw ErrArithmetic.NullDivision;
return x / y;
{ErrSet}after the return type declares the possible errors; multiple:{A, B}.- Without it, the function's error return is
anyerror?. - With it, the error becomes
variant<anyerror, ErrArithmetic>?, anderrin the catch is the unwrapped variant:
i32 res = divide(10, 0) catch err:
switch err:
ErrArithmetic(e):
switch e:
NullDivision: print("Is NullDivision\n");
Negative: print("Is Negative\n");
anyerror(e):
print("Is anyerror\n");
The Error Structure
Every error is { u32 type_id, u32 value_id, str* message }:
// hash of the set name (path-dependent; don't hardcode)
type_id := e.type_id;
// index of the thrown value
value_id := e.value_id;
// context string
message := e.message;
Prefer casting the error to a string: str(e)" -> ErrArithmetic.NullDivision (message not included). If you want to include message write $"{e}: {e.message}" -> ErrArithmetic.NullDivision: Tried to divide 10 by 0.
Error Context
error ErrBase:
B1("Base Error 1"),
B2("Base Error 2");
def somefn():
// any str expression
throw ErrSpecial.S2($"Overwritten message: {override}");
Default messages come from the set definition; a custom message overrides them.
Anonymous Errors
def crash():
// no set definition needed
throw error.Crash("Custom Message");
Anonymous errors share one internal, function-local error set (error.<fn_id>); you can only tell that an error happened, not which one.
Inline Catch
When the catch body is just a switch on the error, drop err and the nested switch:
will_throw() catch:
ErrInline(b): print("is inline thrown\n");
anyerror(a): print("is anyerror\n");
Only works if the function names its error types (switching on a bare anyerror is impossible).