diff --git a/examples.txt b/examples.txt index 67b6320..ec11b9e 100644 --- a/examples.txt +++ b/examples.txt @@ -19,7 +19,7 @@ Structs Methods Interfaces # Embedding -# Errors +Errors Goroutines Channels Channel Buffering diff --git a/examples/errors.sh b/examples/errors.sh new file mode 100644 index 0000000..a04563b --- /dev/null +++ b/examples/errors.sh @@ -0,0 +1,10 @@ +$ go run errors.go +f1 worked: 10 +f1 failed: can't work with 42 +f2 worked: 10 +f2 failed: 42 - can't work with it +42 +can't work with it + +# See this [great post](http://blog.golang.org/2011/07/error-handling-and-go.html) +# on the Go blog for more on error handling in Go. diff --git a/examples/errors/errors.go b/examples/errors/errors.go index 40198aa..48ee27e 100644 --- a/examples/errors/errors.go +++ b/examples/errors/errors.go @@ -1,26 +1,85 @@ -// Errors by return value... +// In Go it's idiomatic to communicate errors via an +// explicit, separate return value. This contrasts with +// the exceptions used in languages like Java and Ruby and +// the overloaded single result / error value sometimes +// used in C. Go's approach makes it easy to see which +// functions return errors and to handle them using the +// same language constructs employed for any other, +// non-error tasks. package main import "errors" import "fmt" -func myFun(arg int) (int, error) { +// By convention, errors are the last return value and +// have type `error`, a built-in interface. +func f1(arg int) (int, error) { if arg == 42 { + + // `errors.New` constructs a basic `error` value + // with the given error message. return -1, errors.New("can't work with 42") } + + // A nil value in the error position indicates that + // there was no error. + return arg + 3, nil +} + +// It's possible to use custom types as `error`s by +// implementing the `Error()` method on them. Here's a +// variant on the example above that uses a custom type +// to explicitly represent an argument error. +type argError struct { + arg int + prob string +} + +func (e *argError) Error() string { + return fmt.Sprintf("%d - %s", e.arg, e.prob) +} + +func f2(arg int) (int, error) { + if arg == 42 { + + // In this case we use `&argError` syntax to build + // a new struct, supplying values for the two + // fields `arg` and `prob`. + return -1, &argError{arg, "can't work with it"} + } return arg + 3, nil } func main() { - r, _ := myFun(7) - fmt.Println(r) - _, e := myFun(42) - fmt.Println(e) + // The two loops below test out each of our + // error-returning functions. Note that the use of an + // inline error check on the `if` line is a common + // idiom in Go code. + for _, i := range []int{7, 42} { + if r, e := f1(i); e != nil { + fmt.Println("f1 failed:", e) + } else { + fmt.Println("f1 worked:", r) + } + } + for _, i := range []int{7, 42} { + if r, e := f2(i); e != nil { + fmt.Println("f2 failed:", e) + } else { + fmt.Println("f2 worked:", r) + } + } + + // If you want to programmatically use the data in + // a custom error, you'll need to get the error as an + // instance of the custom error type via type + // assertion. + _, e := f2(42) + if ae, ok := e.(*argError); ok { + fmt.Println(ae.arg) + fmt.Println(ae.prob) + } } - -// todo: custom errors - -// todo: data conveying errors