Go Custom Error Types: Difference between revisions

From NovaOrdis Knowledge Base
Jump to navigation Jump to search
Line 13: Line 13:
If we need to propagate just one specific value with an error, so we can programmatically handle that value as part of subsequent error handling, aliasing the value's type and turning it into an error type by making it implement the <code>[[Go_Language_Error_Handling#The_error_Type_and_error_Values|error]]</code> interface is a pattern that can be used.
If we need to propagate just one specific value with an error, so we can programmatically handle that value as part of subsequent error handling, aliasing the value's type and turning it into an error type by making it implement the <code>[[Go_Language_Error_Handling#The_error_Type_and_error_Values|error]]</code> interface is a pattern that can be used.


For example, if we want to recover an invalid value passed to the <code>Sqrt()</code> function, we can alias <code>float64</code> to <code>NegaativeSqrtError</code> and have the type implement the <code>error</code> interface:
For example, if we want to recover an invalid value passed to a square root function <code>sqrt()</code> function, we can alias <code>float64</code> to <code>NegaativeSqrtError</code> and have the type implement the <code>error</code> interface:


<syntaxhighlight lang='go'>
<syntaxhighlight lang='go'>

Revision as of 22:16, 28 December 2023

Internal

Overview

The values of any type that implements the error interface can be used as errors. An improvement of error types over error values is their ability to provide more context: instead of a string that contains a human-readable error message, the error instance may carry additional information like (for example) line number, user IDs, etc., which can be used programmatically by error handling code. Error types may even wrap an underlying error in the new type to provide more context. See os.PathError for such an example. As mentioned in the Error Basics section, if context elements need to be processed programmatically as part of the program logic, use a custom type instead of parsing the error message.

As long as the specific type is not used in error handling, in type assertion if and switch statements, custom error types values can be used as any other generic error values and do not introduce additional complications.

However, the problems start when error types must be made public, so the caller can use a type assertion or a type switch. If your code implements an interface whose contract requires a specific error type, all implementors of that interface need to depend on the package that defines the error type. This knowledge of a package's types creates a strong coupling with the caller, making for a brittle API. This is a reason to avoid error types, at least to avoid making them part of the public API.

Type Aliases as Error Types

If we need to propagate just one specific value with an error, so we can programmatically handle that value as part of subsequent error handling, aliasing the value's type and turning it into an error type by making it implement the error interface is a pattern that can be used.

For example, if we want to recover an invalid value passed to a square root function sqrt() function, we can alias float64 to NegaativeSqrtError and have the type implement the error interface:

type NegativeSqrtError float64

func (f NegativeSqrtError) Error() string {
  return fmt.Sprintf("math: square root of a negative number %g", float64(f))
}

func sqrt(arg float64) (float64, error) {
  if arg < 0 {
    return -1, NegativeSqrtError(arg)
  }
  return math.Sqrt(arg), nil
}

...

r, err := sqrt(-1)
if err != nil {
  _, assertionTrue := err.(NegativeSqrtError)
  if assertionTrue {
    fmt.Printf("%v\n", err)
  } else {
    fmt.Println("other kind of error")
  }
} else {
  fmt.Println(r)
}

Structs as Error Types

TODO

Error types is another form of Go error handling. An error type is a type created by implementing the error interface.

Example:

... gocon-spring-2016.pdf

and then use type assertions

err := something()
switch err := err.(type) {case nil:
// call succeeded, nothing to do
 case *MyError:

fmt.Println(error occurred on line:, err.Line)default:
}