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
type error interface {
Error() string
}
os.PathError
// PathError records an error and the operation and
// file path that caused it.
type PathError struct {
Op string // "open", "unlink", etc.
Path string // The associated file.
Err error // Returned by the system call.
}
func (e *PathError) Error() string {
return e.Op + " " + e.Path + ": " + e.Err.Error() // e.g. open /etc/passwx: no such file or directory
}
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:
var user = os.Getenv("USER")
func init() {
if user == "" {
panic("no value for $USER")
}
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.
func server(workChan <-chan *Work) {
for work := range workChan {
go safelyDo(work)
}
}
func safelyDo(work *Work) {
defer func() {
if err := recover(); err != nil {
log.Println("work failed:", err)
}
}()
do(work)
}
recover
always returns nil
unless called DIRECTLY from a deferred function
deferred functions can modify named return values