Go’s multivalue return makes it easy to return a detailed error description alongside the normal return value.

errors have type error, a simple built-in interface

  1. type error interface {
  2. Error() string
  3. }

os.PathError

  1. // PathError records an error and the operation and
  2. // file path that caused it.
  3. type PathError struct {
  4. Op string // "open", "unlink", etc.
  5. Path string // The associated file.
  6. Err error // Returned by the system call.
  7. }
  8. func (e *PathError) Error() string {
  9. return e.Op + " " + e.Path + ": " + e.Err.Error() // e.g. open /etc/passwx: no such file or directory
  10. }

Panic

panic is a built-in function that in effect creates a run-time error that will stop the program.
The function takes a single argument of arbitrary type—often a string—to be printed as the program dies.
It’s also a way to indicate that something impossible has happened, such as exiting an infinite loop.

The library functions should avoid panic.
If the problem can be masked or worked around, it’s always better to let things continue to run rather than taking down the whole program.

One possible counterexample is during initialization:

  1. var user = os.Getenv("USER")
  2. func init() {
  3. if user == "" {
  4. panic("no value for $USER")
  5. }

Recover

When panic is called:

  • it immediately stops execution of the current function
  • and begins unwinding the stack of the goroutine,
  • running any deferred functions along the way.
  • If that unwinding reaches the top of the goroutine’s stack, the program dies.

It is possible to use the built-in function recover to regain control of the goroutine and resume normal execution.
A call to recover stops the unwinding and returns the argument passed to panic.
Because the only code that runs while unwinding is inside deferred functions, recover is only useful inside deferred functions.

One application of recover is to shut down a failing goroutine inside a server without killing the other executing goroutines.

  1. func server(workChan <-chan *Work) {
  2. for work := range workChan {
  3. go safelyDo(work)
  4. }
  5. }
  6. func safelyDo(work *Work) {
  7. defer func() {
  8. if err := recover(); err != nil {
  9. log.Println("work failed:", err)
  10. }
  11. }()
  12. do(work)
  13. }

recover always returns nil unless called DIRECTLY from a deferred function

deferred functions can modify named return values