Introduction
In Go, error handling is explicit and uses the built-in error
interface. While Go provides several ways to create errors, including using the errors
package, there are situations where you might want to create custom error types to provide more context or additional information about the error. In this chapter, you will learn how to create, use, and handle custom errors in Go.
The error Interface
The error
interface in Go is defined as:
type error interface {
Error() string
}
Any type that implements the Error()
method with this signature satisfies the error
interface and can be used as an error.
Creating Custom Errors
To create a custom error, you define a struct to hold the error details and implement the Error()
method for that struct.
Example: Defining a Custom Error
Example:
package main
import (
"fmt"
)
// Define a custom error type
type MyError struct {
Code int
Message string
}
// Implement the Error() method for the custom error type
func (e *MyError) Error() string {
return fmt.Sprintf("Error %d: %s", e.Code, e.Message)
}
func main() {
// Create an instance of the custom error
err := &MyError{Code: 404, Message: "Resource not found"}
// Use the error
fmt.Println(err)
}
Output:
Error 404: Resource not found
Using Custom Errors
You can use custom errors in the same way as built-in errors, by returning them from functions and checking for them in the calling code.
Example: Returning and Checking Custom Errors
Example:
package main
import (
"fmt"
)
// Define a custom error type
type MyError struct {
Code int
Message string
}
// Implement the Error() method for the custom error type
func (e *MyError) Error() string {
return fmt.Sprintf("Error %d: %s", e.Code, e.Message)
}
// Function that returns a custom error
func doSomething() error {
return &MyError{Code: 500, Message: "Internal Server Error"}
}
func main() {
err := doSomething()
if err != nil {
if customErr, ok := err.(*MyError); ok {
fmt.Printf("Custom error occurred: %d - %s\n", customErr.Code, customErr.Message)
} else {
fmt.Println("An error occurred:", err)
}
}
}
Output:
Custom error occurred: 500 - Internal Server Error
Wrapping Errors with Additional Context
Go 1.13 introduced error wrapping, which allows you to wrap errors with additional context using fmt.Errorf
and the %w
verb. This can be combined with custom errors to provide more detailed error information.
Example: Wrapping Custom Errors
Example:
package main
import (
"fmt"
"errors"
)
// Define a custom error type
type MyError struct {
Code int
Message string
}
// Implement the Error() method for the custom error type
func (e *MyError) Error() string {
return fmt.Sprintf("Error %d: %s", e.Code, e.Message)
}
// Function that returns a wrapped custom error
func doSomething() error {
baseErr := &MyError{Code: 500, Message: "Internal Server Error"}
return fmt.Errorf("failed to do something: %w", baseErr)
}
func main() {
err := doSomething()
if err != nil {
var customErr *MyError
if errors.As(err, &customErr) {
fmt.Printf("Custom error occurred: %d - %s\n", customErr.Code, customErr.Message)
} else {
fmt.Println("An error occurred:", err)
}
}
}
Output:
Custom error occurred: 500 - Internal Server Error
Advanced Custom Error Handling with Context and Stack Traces
For more advanced error handling, you might want to include additional context or even stack traces in your custom errors. There are third-party libraries, such as pkg/errors
, that provide enhanced error handling features.
Example: Using pkg/errors for Stack Traces
Example:
package main
import (
"fmt"
"github.com/pkg/errors"
)
// Define a custom error type
type MyError struct {
Code int
Message string
}
// Implement the Error() method for the custom error type
func (e *MyError) Error() string {
return fmt.Sprintf("Error %d: %s", e.Code, e.Message)
}
// Function that returns a wrapped custom error with stack trace
func doSomething() error {
baseErr := &MyError{Code: 500, Message: "Internal Server Error"}
return errors.Wrap(baseErr, "failed to do something")
}
func main() {
err := doSomething()
if err != nil {
fmt.Printf("An error occurred: %+v\n", err)
}
}
Output:
An error occurred: failed to do something: Error 500: Internal Server Error
main.doSomething
/path/to/file.go:15
main.main
/path/to/file.go:20
runtime.main
/usr/local/go/src/runtime/proc.go:203
Conclusion
Creating and using custom errors in Go allows you to provide more detailed and specific error information in your applications. By defining custom error types and implementing the Error()
method, you can tailor error messages to suit your needs. Additionally, by leveraging features like error wrapping and third-party libraries, you can enhance your error handling to include context and stack traces, making it easier to diagnose and fix issues in your code.