Introduction
Error handling in Go is designed to be straightforward and explicit. Instead of using exceptions like many other languages, Go uses a simple, robust error type that allows for explicit error checking. The error
type is a built-in interface that represents an error condition with a single method, Error()
. In this chapter, you will learn the basics of error handling, defining custom error types, and using advanced error handling techniques in Go.
The error Interface
The error
interface is defined in the builtin
package as follows:
type error interface {
Error() string
}
Any type that implements the Error()
method with the above signature satisfies the error
interface and can be used as an error.
Basic Error Handling
Returning and Checking Errors
Functions in Go often return an error as the last return value. You can check for the error and handle it appropriately.
Example:
package main
import (
"errors"
"fmt"
)
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("cannot divide by zero")
}
return a / b, nil
}
func main() {
result, err := divide(4, 0)
if err != nil {
fmt.Println("Error:", err)
} else {
fmt.Println("Result:", result)
}
}
Creating Custom Errors
You can create custom error types by defining a struct and implementing the Error()
method.
Example:
package main
import (
"fmt"
)
// Define a custom error type
type DivideError struct {
Dividend float64
Divisor float64
}
// Implement the Error() method for the custom error type
func (e *DivideError) Error() string {
return fmt.Sprintf("cannot divide %f by %f", e.Dividend, e.Divisor)
}
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, &DivideError{a, b}
}
return a / b, nil
}
func main() {
result, err := divide(4, 0)
if err != nil {
fmt.Println("Error:", err)
} else {
fmt.Println("Result:", result)
}
}
Using fmt.Errorf
The fmt.Errorf
function can be used to format error messages with additional context.
Example:
package main
import (
"fmt"
)
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, fmt.Errorf("cannot divide %f by %f", a, b)
}
return a / b, nil
}
func main() {
result, err := divide(4, 0)
if err != nil {
fmt.Println("Error:", err)
} else {
fmt.Println("Result:", result)
}
}
Using the errors Package
The errors
package provides functions to create and work with errors.
errors.New
Creates a new error with a specified message.
Example:
package main
import (
"errors"
"fmt"
)
func main() {
err := errors.New("an error occurred")
fmt.Println("Error:", err)
}
errors.Is
and errors.As
Starting with Go 1.13, the errors
package introduced the errors.Is
and errors.As
functions for improved error handling and inspection.
Example:
package main
import (
"errors"
"fmt"
)
// Define a custom error type
type MyError struct {
Msg string
}
func (e *MyError) Error() string {
return e.Msg
}
func main() {
var err error = &MyError{"something went wrong"}
if errors.Is(err, &MyError{}) {
fmt.Println("MyError occurred:", err)
} else {
fmt.Println("Different error occurred")
}
}
errors.Unwrap
The Unwrap
function returns the result of calling the Unwrap
method on an error, which allows you to retrieve the underlying error.
Example:
package main
import (
"errors"
"fmt"
)
func main() {
err1 := errors.New("original error")
err2 := fmt.Errorf("wrapped error: %w", err1)
fmt.Println("Error:", err2)
fmt.Println("Unwrapped Error:", errors.Unwrap(err2))
}
Conclusion
Error handling in Go is designed to be explicit and straightforward. By understanding how to use the built-in error
type, create custom errors, and leverage the functions in the errors
package, you can effectively manage error conditions in your Go programs. This approach ensures that error handling is clear and consistent, leading to more robust and maintainable code.