Essential Error Handling
In this section we will go over the essential information about Optionals and safe methods for working with them, for example
if (catch optional_value)
and the Rethrow operator !
.
In the advanced section there are other nice to have features.
Like an alternative to safely unwrap a result from an Optionals using
if (try optional_value)
and an unsafe method to force unwrap !!
a result from an Optional, return default values for optionals ??
if they are empty and other more specialised concepts.
What is an Optional?
Section titled “What is an Optional?”Optionals are a safer alternative to returning -1
or null
from
a function, when a valid value can’t be returned. An Optional
has either a result or is empty. When an Optional
is empty it has an Excuse
explaining what happened.
- For example trying to open a missing file returns the
Excuse
ofio::FILE_NOT_FOUND
. - Optionals are declared by adding
?
after the type. - An
Excuse
is of typefault
.
int? a = 1; // Set the Optional to a result
The Optional Excuse is set with ?
after the value.
// Set the Optional to empty with a specific Excuse.int? b = io::FILE_NOT_FOUND?;
🎁 Unwrapping an Optional
Section titled “🎁 Unwrapping an Optional”Checking if an Optional is empty
Section titled “Checking if an Optional is empty”import std::io;
fn void? test(){ // Return an Excuse by adding '?' after the fault. return io::FILE_NOT_FOUND?;}
fn void main(String[] args){ // If the Optional is empty, assign the // Excuse to a variable: if (catch excuse = test()) { io::printfn("test() gave an Excuse: %s", excuse); }}
Automatically unwrapping an Optional result
Section titled “Automatically unwrapping an Optional result”If we escape the current scope from an if (catch my_var)
using a return
, break
, continue
or Rethrow !
,
then the variable is automatically unwrapped to a non-Optional:
fn void? test(){ int? foo = unreliable_function(); if (catch excuse = foo) { // Return the excuse with `?` operator return excuse?; } // Because the compiler knows 'foo' cannot // be empty here, it is unwrapped to non-Optional // 'int foo' in this scope: io::printfn("foo: %s", foo); // 7}
Using the Rethrow operator !
to unwrap an Optional value
Section titled “Using the Rethrow operator ! to unwrap an Optional value”- The Rethrow operator
!
will return from the function with theExcuse
if the Optional result is empty. - The resulting value will be unwrapped to a non-Optional.
import std::io;
// Function returning an Optionalfn int? maybe_func() { /* ... */ }
fn void? test(){ // ❌ This will be a compile error // maybe_function() returns an Optional // and 'bar' is not declared Optional: // int bar = maybe_function();
int bar = maybe_function()!; // ✅ The above is equivalent to: // int? temp = maybe_function(); // if (catch excuse = temp) return excuse?
// Now temp is unwrapped to a non-Optional int bar = temp; // ✅ This is OK}
⚠️ Optionals affect types and control flow
Section titled “⚠️ Optionals affect types and control flow”Optionals in expressions produce Optionals
Section titled “Optionals in expressions produce Optionals”Use an Optional anywhere in an expression the resulting expression will be an Optional too.
import std::io;
fn void main(String[] args){ // Returns Optional with result of type `int` or an Excuse int? first_optional = 7;
// This is Optional too: int? second_optional = first_optional + 1;}
Optionals affect function return types
Section titled “Optionals affect function return types”import std::io;
fn int test(int input){ io::printn("test(): inside function body"); return input;}
fn void main(String[] args){ int? optional_argument = 7;
// `optional_argument` makes returned `returned_optional` // Optional too: int? returned_optional = test(optional_argument);}
Functions conditionally run when called with Optional arguments
Section titled “Functions conditionally run when called with Optional arguments”When calling a function with an Optionals as arguments, the result will be the first Excuse found looking left-to-right. The function is only executed if all Optional arguments have a result.
import std::io;
fn int test(int input, int input2){ io::printn("test(): inside function body"); return input;}
fn void main(String[] args){ int? first_optional = io::FILE_NOT_FOUND?; int? second_optional = 7;
// Return first excuse we find int? third_optional = test(first_optional, second_optional); if (catch excuse = third_optional) { // excuse == io::FILE_NOT_FOUND io::printfn("third_optional's Excuse: %s", excuse); }}
Interfacing with C
Section titled “Interfacing with C”For C the interface to C3:
- The
Excuse
in the Optional of typefault
is returned as the regular return. - The result in the Optional is passed by reference.
For example:
// C3 code:fn int? get_value();
// Corresponding C code:c3fault_t get_value(int *value_ref);
The c3fault_t
is guaranteed to be a pointer sized value.