Results and errors
In AEL, the Agent Engineering Language, a value that may be missing is an Option, and an operation that can fail returns a Result. There is no null value and there are no exceptions: an error is an ordinary typed value that your code handles, passes on or turns into another error.
Option
| Variant | Meaning |
|---|---|
Option::Some(value) | A value of type T is present. |
Option::None | No value. Option::None is itself a value, written without parentheses. |
Use Option<T> for a setting that may be left out, a search that may find nothing, or a field that is filled in later.
fn first_positive(values: [i32; 3]) -> Option<i32> {
let mut i: u32 = 0;
while i < 3 {
if values[i] > 0 {
return Option::Some(values[i]);
}
i = i + 1;
}
return Option::None;
}
Result
| Variant | Meaning |
|---|---|
Result::Ok(value) | The operation succeeded with a value of type T. |
Result::Err(error) | The operation failed with an error of type E. |
An operation that succeeds without a value returns Result<(), E>, and its success is Result::Ok(()).
Handling an error
You take a Result apart with match, which must handle both variants. To pass an error on, return it from your own function; to replace it, return a different error.
enum LookupError {
NotFound,
Invalid(u16),
}
fn discount(code: u16) -> Result<u32, LookupError> {
if code == 0 {
return Result::Err(LookupError::Invalid(code));
}
if code > 500 {
return Result::Err(LookupError::NotFound);
}
return Result::Ok(10);
}
fn price_after(price: u32, code: u16) -> Result<u32, LookupError> {
match discount(code) {
Result::Ok(percent) => {
return Result::Ok(price - price / 100 * percent);
}
Result::Err(error) => {
return Result::Err(error);
}
}
}
- An error type is usually an enum of your own, and its variants can carry values, such as the code in
Invalid(u16). - Nothing is thrown and nothing is retried unless your code retries it.
- Binding a value in a
matcharm moves it out of the matched value; see Ownership.
Built-in error types
Four error types are built in. Each is an enum whose values copy like integers.
| Type | Variants | Returned by |
|---|---|---|
CapacityError | Full | Appending text that does not fit. |
IndexError | OutOfBounds | Removing a list value at an index that does not exist; reading a byte of text past its end. |
CastError | OutOfRange | Converting an integer to a type it does not fit. |
StringError | InvalidUtf8, Full | Making text from bytes that are not valid UTF-8, or do not fit. |
Text and collections and Numbers list the operations.
Errors that give your value back
When an operation cannot take a value you pass it, the error hands the value back, so it is never lost and never owned twice:
- adding to a full list returns
Result<(), T>, and the error holds your value; - sending a message to a full or closed mailbox returns the message to you; see Concurrency.
In both cases the collection or mailbox stays exactly as it was: nothing is half-added.
Errors from the rest of the library
Operations that reach outside the program return typed results that keep different failures apart, so your code can react to each:
- Models: an unsupported parameter, input or capability, a missing binding or credential, an input that is too large, an incomplete output, and usage that could not be counted. See Model client.
- HTTP: a connection or TLS failure, a timeout, a cancellation, a body over its limit. See HTTP client.
- Programs you run: a non-zero exit, a timeout, a failure to start, output that could not be read, a cancellation, a resource limit. See Files and processes.
When a failure leaves it unknown whether an effect outside the program happened, such as a payment sent before a connection dropped, the result says the effect is uncertain rather than report success or failure. Your code decides how to reconcile it; AEL does not repeat such an operation on its own.
Results of subagents
An agent spawns subagents at run time. What comes back from them is an ordinary typed result:
try_spawnreturnsResult<Spawned<R>, SpawnRefusal>: the handle of the subagent, or the reason it could not start, such asSpawnRefusal::BudgetUnfundable.joinreturnsResult<R, SpawnFailure>: the subagent's result, or how it ended without one, such asSpawnFailure::TimedOut.- A subagent's failure is a value that your code handles like any
Result::Err. It never fails the code that spawned the subagent by itself. SpawnFailure::Unknownis kept when the subagent's outcome cannot be established: no retry and no new spawn turns it into a success or a failure.
Subagents lists every refusal and failure, and Types the three types.
Errors, panics and diagnostics
| Kind | When | What happens |
|---|---|---|
| Diagnostic | You check or build your code. | Nothing runs until you fix the source. |
Result error | An operation fails in a way your program expects. | The operation returns Result::Err, which you handle. |
| Panic | A defect: an overflow, a division by zero, an index out of range. | The program stops with a failure report. A panic is never caught as a Result. |
Errors and checked arithmetic describes panics, and Concurrency how agents recover from faults.