Documentation
¶
Overview ¶
package goerror provides for creation of semantic error types. Using the stdlib error package, when trying to discern the error type returned by a function, we can either (a) compare to a well-known error reference (for example, io.EOF), or (b) we have to parse the actual error message. The former, e.g. io.EOF, is fine for basic cases, but obviously won't allow for call-site specific information in the error.
For example, let's say we create an error for asserting input arg correctness, something like an AsssertError, or IllegalArgumentError. We can try pattern (a) per io.EOF, in which case we can certainly return that error, but can't provide additional info such as which precise arg caused the error. Or we can return a plain jane error with a formatted message, in which case we can't immediately tell what 'kind' of error was returned.
This package addresses this concern by providing error 'types' that can be generically defined at (some) package level and then used with explicit additional details.
Errors are created using 'Define'. (Note, not 'New', since this merely defines an error type).
var (
TerribleError = goerror.Define("TerribleError")
NotSoTerribleError = goerror.Define("NotSoTerribleError")
)
Such error types can then be 'instantiated' using the defintion, wherever one would normally create and/or return a generic error.
// function foo may return either TerribleError or
// NotSoTerribleError
func foo() error {
// ...
if flipcoin() {
return TerribleError("an example usage")
}
return NotSoTerribleError() // detailed info is optional
}
And in the functional callsite, we can specifically check to see what type of error we got.
if e := foo(); e != nil {
switch typ := goerror.TypeOf(e); {
case typ.Is(TerribleError):
/* handle it */
case typ.Is(NotSoTerribleError):
/* handle it */
}
}
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
Types ¶
type Error ¶
type Error struct {
// contains filtered or unexported fields
}
Note that this type is exported *only* in order to surface Is() to package docs. Otherwise, package users should not directly use this type.
Example ¶
Example defining, returning, and checking goerror
package main
import (
"fmt"
"goerror"
)
// Let's define a few canonical goerror
var (
IllegalArgument = goerror.Define("IllegalArgument")
IllegalState = goerror.Define("IllegalState")
AccessDenied = goerror.Define("AccessDenied")
Bug = goerror.Define("BUG")
)
// Example defining, returning, and checking goerror
func main() {
user := "theuser"
oldpw := "old-secret"
newpw := "new-secret"
if e := ChangePassword(user, oldpw, newpw); e != nil {
switch typ := goerror.TypeOf(e); {
case typ.Is(IllegalArgument): /* handle it */
case typ.Is(IllegalState): /* handle it */
case typ.Is(AccessDenied): /* handle it */
default: /* this violates the API contract - must be a bug */
panic(Bug(fmt.Sprintf("unexpected error %v returned by ChangePassword()", e)))
}
}
}
// (Example function that returns categorical goerror.)
// Change the user's password.
//
// returns IllegalArgument for any nil input;
// IllegalState if user not logged in;
// and AccessDenied if user and credentials don't match.
func ChangePassword(user, oldPassword, newPassword string) error {
// assert args
if user == "" {
return IllegalArgument("user is nil")
}
if oldPassword == "" {
return IllegalArgument("oldpassword is nil")
}
if newPassword == "" {
return IllegalArgument("newPassword is nil")
}
// user must be already logged in to change passwords
// (it's just an example ;-)
if !UserLoggedIn(user) {
return IllegalState("user must be logged in to change pw")
}
// verify user and oldpassword match
// (Yes, bad idea to leak this info but it is an example of using
// root cause errors. cheer up.)
if e := CheckAuthorized(user, oldPassword); e != nil {
return AccessDenied().WithCause(e)
}
// ...
return nil
}
func UserLoggedIn(user string) bool {
// ...
return false
}
func CheckAuthorized(user, pw string) error {
// ...
return fmt.Errorf("unauthorized")
}
func TypeOf ¶
Returns an Error, typically for use in conjunction with the Error#Is(). Function name is as such to allow for a readable call site, as below:
if goerror.TypeOf(e).Is(AssertionError)
If the input arg 'e' is a plain (builtin) error, it is converted to a goerror.Error pointer.
func (*Error) WithCause ¶
Associate a root cause error with the given error. If cause is already set, subsequent calls to this function are ignored.
Typcial usage pattern:
var WriteError = goerror.Define("Write Error")
func writeBuffer(..) error {
...
// let's pretend we did io and got an error 'ioerror'
return WriteError("in writeBuffer").WithCause(ioerror)
}
