Error Handling
Error handling is making use of the enum feature of the language to embed
error information into the return value of a function. Error codes can
either be checked and handled manually, or they can be handled indirectly using
an implicit or explicit try/catch statement. As such, they provide much of
the same syntactic leanness of true exceptions, while also providing the
portability and performance of using simple error codes.
Example
enum DecodeError {
unternimatedSequence,
invalidSequence,
maximumLengthExceeded
}
@error(DecodeError)
function decode(text)
{
var ret: string;
for (i in 0 .. text.length) {
if (text[i] == '\\') {
if (i + 1 < text.length)
throw DecodeError.unterminatedEscapeSequence;
ret ~= decodeSequence(text[i+1]);
i++;
} else ret ~= text[i];
}
return ret;
}
function main()
{
let str = "Hello, World\\";
// manually check for errors
let res = decode(str);
assert(!res.success)
assert(res.error == DecodeError.unterminatedSequence);
// use exception style error handling - note the "!" operator
try {
print("Decoded: ", decode(str)!);
} catch (error: DecodeError) {
print("Error decoding string: ", error);
}
}
Handling errors
Manual Checks: The error or success result of a function can be accessed by using the
errororunwrapproperties of the return value:var res = readFileUTF8("text.txt"); if (res.success) print("File contents: ", res.unwrap); else print("Error: ", res.error);Note that the
.successproperty must be checked to betruebefore accessing the.unwrapproperty and must be checked to befalsebefore accessing the.errorproperty. Failing to do so will result in an abortion of the process.Exception Style: In cases where checking for each error individually is unnecessary or undesirable, the
!postfix operator provides a shortcut syntax that results in exception-like behavior when used together withtry/catch:try { var res1 = readFileUTF8("file1.txt")!; var res2 = readFileUTF8("file2.txt")!; print("Combined contents: ", res1 ~ res2); } catch (e) print("Failed to read input files: ", e);
Function Error Annotations
Any function that might either directly, using throw, or indirectly, using the
! operator, throw an error must be annotated with an @error annotation. The
enum type passed to the annotation must be a superset of all error types that
might be thrown.
Even if errors may be generated inside, functions do not need to specify
an @error annotation if they handle all errors by checking return values,
or enclose all possibly throwing code with try {} catch {} clauses that
handle all possible errors.
Combining Multiple Error Enums
The | operator can be used to combine two or more error enums into a
combined error type that can hold any value that is representable by either of
those enums.
Example
@error(IOError | DecodeError)
function loadEncodedFile(path)
{
var encoded = readFileUTF8(path)!;
return decode(encoded)!;
}
function main()
{
// Catch by error-sub type:
try
print(loadEncodedFile("text.txt"));
catch (error: IOError)
print("The file could not be read: ", error);
catch (error: DecodeError)
print("The encoding of the file is incorrect: ", error);
// Catch by combined type:
try
print(loadEncodedFile("text.txt"));
catch (error: (IOError | DecodeError))
print("Error reading encoded file: ", error);
}
Return Value of @error Functions
The return value of any function that has an @error annotation will be
augmented by the compiler with the optional error value. This is done by
mapping the plain return type T to the type
core.error.ErrorReturn(T, ErrorType), where ErrorType is the type
passed to the @error annotation.
ErrorReturn supports the following primitives for working with error results:
- `success` property: Any
ErrorReturnvalue can be checked for success by checking the Boolean.successproperty. A value oftrueindicates success, whilefalseindicates an error. Depending on this value, the.unwrapor.errorproperties may be accessed. - `error` Property: This property can be used to access the value of the
returned error. Must only be called of the Boolean value of the return
value is
falseand will abort the process otherwise. - `unwrap` Property: This property can be used to access the value of the
function's original return value. Must only be called of the Boolean
value of the return value is
trueand will abort the process otherwise. - `!` Postfix Operator: Returns the original return value of the function in case of success and throws the error value otherwise. Using this operator is the typical approach to error handling when using exception style programming.
The Implicit Try-Catch
Any function with an @error annotation will be wrapped inside of a
compiler-generated try/catch statement that catches any uncaught error
and returns it as part of the function's ErrorReturn return value.