Custom Errors in Nim

import strformat

# A custom error type usually has the suffix "Error".
type ArgError = ref object of CatchableError
  arg: int
  message: string

# Adding this `$` method makes `ArgError` implement
# the `Error` interface.
proc `$`(e: ArgError): string =
  fmt"{e.arg} - {e.message}"

proc f(arg: int): tuple[result: int, error: ref CatchableError] =
  if arg == 42:
    # Return our custom error.
    return (-1, ArgError(arg: arg, message: "can't work with it"))
  return (arg + 3, nil)

proc main() =
  # In Nim, we use exception handling to deal with errors
  try:
    let (_, err) = f(42)
    if err != nil:
      let ae = ArgError(err)
      echo ae.arg
      echo ae.message
    else:
      echo "No error occurred"
  except ArgError as ae:
    echo ae.arg
    echo ae.message
  except:
    echo "An unexpected error occurred"

main()

This example demonstrates how to create and use custom errors in Nim. Here’s a breakdown of the code:

  1. We define a custom error type ArgError that inherits from CatchableError, Nim’s base class for catchable exceptions.

  2. We implement a $ method for ArgError, which is Nim’s equivalent of a toString method. This allows us to easily convert our error to a string representation.

  3. The f function demonstrates how to return a custom error. In Nim, we typically use tuples to return multiple values, including potential errors.

  4. In the main function, we show how to handle these custom errors using Nim’s exception handling mechanism.

  5. We use a try-except block to catch and handle our custom ArgError.

To run this program, save it as custom_errors.nim and use the Nim compiler:

$ nim c -r custom_errors.nim
42
can't work with it

This example showcases Nim’s approach to error handling, which combines return values and exceptions. While it doesn’t have a direct equivalent to Go’s errors.As, Nim’s exception handling and pattern matching provide similar functionality.