I have been reading the source of the JSON library and there is this
case of error handling I cannot wrap my head around:
```
export fn load(src: io::handle, opts: load_option...) (value | error) = {
...
return _load(&lex, 0, limit);
};
fn _load(lexer: *lexer, level: uint, limit: uint) (value | error) = {
const tok = mustscan(lexer)?;
...
```
Here we call `_load` from `load`. In `_load`, if an error occurs in
`mustscan`, we pass it on to `load`. But then in `load` there is no
handling for errors coming from `_load`.
In the tutorial, when calling a function in the return statement, it is
always handled (with the exception of `strerror`). So, how does the
error get handled in this example? Is it passed further up from `load`
implicitly?
On Fri Jan 20, 2023 at 8:48 PM GMT, KAAtheWise wrote:
> I have been reading the source of the JSON library and there is this
> case of error handling I cannot wrap my head around:
>
> ```
> export fn load(src: io::handle, opts: load_option...) (value | error) = {
> ...
> return _load(&lex, 0, limit);
> };
>
> fn _load(lexer: *lexer, level: uint, limit: uint) (value | error) = {
> const tok = mustscan(lexer)?;
> ...
> ```
>
> Here we call `_load` from `load`. In `_load`, if an error occurs in
> `mustscan`, we pass it on to `load`. But then in `load` there is no
> handling for errors coming from `_load`.
> So, how does the error get handled in this example?
It isn't handled.
> Is it passed further up from `load` implicitly?
Yes, but not by magic.
The _load() function call expression evaluates to a value of type
`(value | error)`, and that value is simply returned with the `return`
statement. Errors are just values too. It's the same as:
const result: (value | error) = _load(...);
return result;
The user of load() is expected to handle its return value however they
want, be it with `!`, `match`, etc. or not at all.
> In the tutorial, when calling a function in the return statement, it is
> always handled (with the exception of `strerror`).
Note that in the many examples in the tutorial, like bufio::scanline(),
you don't have to "handle" the error value with something like `!` in
that line of code, but then you'll have a value of type (thing | error),
which is useless to the next line of code. So of course, you handle it
there because you want to get just the useful value, not the error
value.
I tested it out further and I think I now understand how errors a bit
better.
Is it correct that in the following example the `?` operator does not
change how the following function behaves? Because if `stou` returns an
error, it is immediately returned, but without the question mark this
error would be returned regardless. At least if the error types behave
just like any other type.
fn getnumber() (uint | error) = {
...
return strconv::stou(num)?;
};