Error Handling

Hush implements two error mechanisms: panics and errors.

Panic

A Panic is an irrecoverable1 error, and will occur when there is a logic issue in your code. Panics will cause the whole program to crash, and execution will be terminated.

  1. let x = 1 / 0
  2. std.print(x)

Running the above script, you’ll get:

  1. Panic in <stdin> (line 2, column 10): division by zero

Examples of errors that cause a panic:

  • Syntax error.
  • Integer division by zero.
  • Index out of bounds.
  • Attempt to call a value that is not a function.
  • Missing or exceeding arguments in function call.

Error

Recoverable errors may be expressed through values of the error type. This is a special type in the language, and it is the only which cannot be expressed through a literal. Values of the error type can only be created using the std.error function, which expects a string description and a arbitrary context:

  1. function check_limit(x)
  2. if x > 10 then
  3. std.error("x cannot be bigger than 10", x)
  4. else
  5. x
  6. end
  7. end

One can then check whether the function has returned an error:

  1. let result = check_limit(11)
  2. if std.type(result) == "error" then
  3. std.print("Error: ", result)
  4. else
  5. std.print("Success: ", result)
  6. end

The description and context fields can be accessed as in a dictionary, but unlike in dictionaries, those cannot be assigned to:

  1. let error = std.error("oh no!", 42)
  2. std.print(error.description) # oh no!
  3. error.description = "you shouldn't do that!" # panic

Examples of errors should be recoverable:

  • File not found.
  • Insufficient permission
  • Invalid format
  • Command not found
  • Command returned non-zero exit status

Try operator

The try (?2) operator may be used to early return from a function if an error occurs. It is nothing but syntax sugar for an if expression, and therefore it may be used in any expression:

  1. function safe_div_mod(x, y)
  2. if y == 0 then
  3. std.error("division by zero", nil)
  4. else
  5. @[ div: x / y, mod: x % y ]
  6. end
  7. end
  8. # The following are equivalent:
  9. function foo()
  10. let result = safe_div_mod(5, 0)
  11. let value = if std.type(result) == "error" then
  12. return result
  13. else
  14. result
  15. end
  16. std.print(value)
  17. end
  18. function bar()
  19. let value = safe_div_mod(5, 0)?
  20. std.print(value) # this won't be executed, as `?` will trigger an early return.
  21. end
  22. # The `?` operator may be used in any expression:
  23. function run()
  24. std.print("div: ", safe_div_mod(5, 0)?.div)
  25. end

1 One can actually catch a panic using the std.catch function, but that should be used sparingly.

2 If you’re familiar with both languages, Hush‘s try operator might feel like the unholy child of Rust’s ? operator and Go’s if err != nil { return err }.