Does Go have standard Err variables?
There are a few common idiomatic ways for a package author to make error returns.
Fixed error variables, usually named
Err…
var ( ErrSomethingBad = errors.New("some string") ErrKindFoo = errors.New("foo happened") )
Error types, usually named
…Error
type SomeError struct { // extra information, whatever might be useful to callers // (or for making a nice message in `Error()`) ExtraInfo int } type OtherError string func (e SomeError) Error() string { /* … */ } func (e OtherError) Error() string { return fmt.Sprintf("failure doing something with %q", string(e)) }
With Go 1.13 and later you may also want to implement a
Unwrap() error
method for use witherrors.Unwrap
.Ad hoc
errors.New
values as needed.func SomepackageFunction() error { return errors.New("not implemented") }
Using errors defined in the standard packages. Usually limited to a small set such as
io.EOF
; in most cases it's better to create your own via method 1 above.func SomeFunc() error { return io.EOF }
Note that sometimes when implementing an interface (such as a
Read
method to become anio.Reader
) it is best to use matching errors (or "required" by the specification of the interface).Making an interface such as
net.Error
:type Error interface { error Timeout() bool // Is the error a timeout? Temporary() bool // Is the error temporary? }
With Go 1.13 or later, returning an existing error with simple context (for more complicated context, use a custom error type with an
Unwrap()
method):func SomepackageFunction() error { err := somethingThatCanFail() if err != nil { return fmt.Errorf("some context: %w", err) } }
Note the new (to Go 1.13) formatting verb
%w
, it wraps the provided error so that callers can get at it witherrors.Unwrap
orerror.Is
.
Often you'll use a mix of all these ways.
The first, second, and fifth are preferred if you think any user of your package will ever want to test for specific errors. They allow things like:
err := somepkg.Function()
if err == somepkg.ErrSomethingBad {
// …
}
// or for an error type, something like:
if e, ok := err.(somepkg.SomeError); ok && e.ExtraInfo > 42 {
// use the fields/methods of `e` if needed
}
For Go 1.13 or later, the above can instead be written as:
err := somepkg.Function()
if errors.Is(err, somepkg.ErrSomethingBad) {
// …
}
// or for an error type, something like:
var someErr somepkg.SomeError
if errors.As(err, &someErr) && someErr.ExtraInfo > 42 {
// use the fields/methods of `someErr` if needed
}
the difference is that errors will be unwrapped as needed.
The fifth way (which is just an extension of the second) allows checking the error for behaviour/type like so (or using Go 1.13's errors.As
):
if e, ok := err.(net.Error); ok && e.Timeout() {
// it's a timeout, sleep and retry
}
The problem with the third way is it leaves no sane way for a user of the package to test for it. (Testing the contents of the string returned by err.Error()
isn't a great idea).
However, it's fine for the errors that you don't ever expect anyone to want to test for.
Further reading:
- Effective Go on errors
- The Go Blog Error handling and Go
- Dave Cheney Inspecting Errors
- Peter Bourgon Programming with errors
No, there aren't. Just provide intelligible errors instead of generic ones. What information does a IllegalArgument transport? Not much, not enough.
As you have seen, there are specific errors that specific packages use. For example, in the database/sql package, they define:
var ErrNoRows = errors.New("sql: no rows in result set")
So if you do QueryRow
(which defers the error until Scan
), and then Scan
, you can do
if err := row.Scan(&data); err != nil && err != sql.ErrNoRows {
//something actually went wrong
} else if err != nil {
//no results
} else {
//we found it
}
os/exec has var ErrNotFound = errors.New("executable file not found in $PATH")
encoding/json has a type UnmarshalTypeError
which is just a type that implements the error
interface.
So no, while there is no "set of standard errors", you can (and most likely should) have specific error variables that you reuse.
You could have your own errorMsgs package that you use, where you can reuse common errors:
err := doSomething(); if err != nil {
switch err {
case errorMsgs.IllegalArgument:
//do something
case errorMsgs.CouldNotConnect:
//do something else
}
}