
错误类型可以帮助我们更好地理解错误的具体信息。例如,我们可以使用 `errors.Unwrap()` 方法来获取错误的原始错误。
Go 语言提供了多种错误处理的最佳实践,我们可以通过这些实践来优化我们的错误处理逻辑。
Error Handling Best Practices
Error handling is a crucial aspect of writing robust and maintainable Go code. It's essential to handle errors in a way that's both informative and respectful of the caller's time.
There are three primary scenarios to consider when handling errors in Go: function internal error handling, function/module error information return, and service/system error information return. Each scenario has its own nuances and best practices.
Here are some key takeaways to keep in mind:
- When creating an error, consider whether it's necessary to include a stacktrace. If the error is a return value, a stacktrace may not be needed. However, if the error is an exceptional result, a stacktrace can be helpful.
- Use libraries like github.com/pkg/errors to create errors with stacktraces. This can be particularly useful when debugging issues.
- When printing errors, use the %+v format specifier to include the stacktrace. Some logging libraries, like go.uber.org/zap, can automatically recognize errors from github.com/pkg/errors and print the stacktrace.
By following these best practices, you can write more robust and maintainable error handling code in Go.
Self-new (may include stacktrace)
When creating your own error, you have the option to include a stacktrace or not. This decision depends on how you want to use the error. If you're treating the error as a return value, a stacktrace might not be necessary. However, if you're treating it as an exceptional result, including a stacktrace can be helpful.
Recommended read: Golang Create Error
You can use the `errors.New` function from the `github.com/pkg/errors` package to create a new error that may include a stacktrace. For example, if you're closing an order and the database is down, you might return an error with a stacktrace like this: `return errors.New("数据库宕机")`.
Note that whether to include a stacktrace or not is entirely up to you. The `wrap` function is used to add a stacktrace to an existing error.
Opaque
Opaque errors are a flexible and decoupled way of handling errors, where the caller only knows whether the operation succeeded or failed, without knowing the details of the error. This approach is particularly useful when interacting with external systems, such as networks, where the caller may need to investigate the nature of the error to determine whether to retry the operation.
In Go, this approach is demonstrated by the github.com/pkg/errors package, which can be used to construct errors that do not carry a stack trace. However, when using this package, special formatting is required to print the stack trace, such as using fmt.Printf("%+v", errors.New("abc")).
Suggestion: Create a Package in Golang
The go.uber.org/zap library takes this a step further by automatically recognizing errors from github.com/pkg/errors and printing the stack trace, provided the errVerbose field is set.
In some cases, a more nuanced approach is required, where the caller needs to investigate the nature of the error. In such cases, the opaque error approach can be combined with a Temporary() method, which allows the caller to inspect the error without revealing its underlying type. This approach is demonstrated in the following example:
By using opaque errors and Temporary() methods, developers can decouple their code from the specific error types and focus on the behavior of the error, rather than its implementation details.
Wrapping Return Values
When wrapping return values, it's essential to remember that you should only handle errors once. Handling an error means inspecting the error value, and making a single decision.
You should wrap third-party library returned errors to add context and preserve the stack trace. This can be done using the `errors.Wrap` function from the `github.com/pkg/errors` package.
The `errors.Wrap` function will add the stack trace to the error, making it easier to debug. You can also use `errors.WithStack` for a similar effect.
Here are the steps to follow when wrapping return values:
- In your application code, use `errors.New` or `errors.Errorf` to return errors.
- If calling other package functions, simply return the error.
- If collaborating with third-party libraries, consider using `errors.Wrap` or `errors.Wrapf` to save the stack information.
- Non-root error-producing locations should directly return the error, rather than logging it at every error-producing point.
- At the top of your program or worker goroutine (request entry), use `%+v` to log the stack details.
- Use `errors.Cause` to get the root error and perform sentinel error checks.
By following these steps, you can effectively handle errors and ensure that your application remains in a consistent state.
Use Only for Exceptional Cases
In Go, error values are used to indicate an abnormal state. This means we should use them sparingly, only for exceptional cases.
Go code explicitly uses error values to indicate an abnormal state, as mentioned in the language's documentation. This distinction is crucial in writing robust and maintainable code.
When handling errors in Go, it's essential to remember that error values should not be used as a regular return value. This approach helps prevent errors from becoming a norm.
The Go language itself emphasizes the use of error values for exceptional situations, not as a standard way to return values. This mindset is vital in writing clean and efficient code.
A fresh viewpoint: Golang Go
Library Implementation
In the Go standard library, the `errors` package provides a way to handle errors in a more robust and elegant way. Specifically, the `Is` method allows you to check if an error is a specific type, and the `As` method allows you to convert an error to a specific type.
The `Is` method is implemented in the `errors` package, and it takes two arguments: the error to check and the target error type. If the target error type is nil, the method returns true if the error is nil, and false otherwise. Otherwise, it checks if the error is comparable to the target error type, and if so, it recursively checks each field of the error until it finds a match or reaches the end of the error chain.
The `As` method is also implemented in the `errors` package, and it takes two arguments: the error to convert and the target interface type. If the target interface type is nil, the method panics. Otherwise, it checks if the error is assignable to the target interface type, and if so, it assigns the error to the target interface and returns true. If the error is not assignable, it recursively unwraps the error until it finds a match or reaches the end of the error chain.
A unique perspective: Convert a Map to Json Golang
The `Unwrap` method is used to recursively unwrap an error until it reaches the end of the error chain. This is useful when you need to handle nested errors.
Here are some key takeaways from the `errors` package implementation:
- The `Is` method checks if an error is a specific type, and returns true if it is, and false otherwise.
- The `As` method converts an error to a specific type, and returns true if the conversion is successful, and false otherwise.
- The `Unwrap` method recursively unwraps an error until it reaches the end of the error chain.
These methods provide a powerful way to handle errors in Go, and can be used to write more robust and maintainable code.
Handling gRPC Errors Gracefully
gRPC errors can be tricky to handle, but there are ways to do it gracefully. In gRPC, errors are sent as a text string over the network, which is then deserialized into an error object on the client side. However, this deserialization process creates a new error object, making it difficult to compare errors using the `error.Is` method.
To handle gRPC errors, you can use the `status.FromError` method to convert the error into a `status.Status` object, which can be written to the response headers. The client can then parse these headers to extract the error information.
Intriguing read: Read a Custom Resource Using Cynamic Client Golang
A common issue with gRPC errors is determining whether to base the error handling on the error code or the error message. To address this, you can use the `WithDetails` method to embed business-specific details into the error code. This allows you to provide more context to the client about the error.
Here's an example of how to use `WithDetails` to create a custom error code:
- Use the `status` package to create the desired error code and corresponding gRPC framework error details.
- Use the `Google API` package to set more detailed business error details.
For instance, if you need to handle invalid ID requests, you can define a custom error code like `BadRequest_FieldViolation` and return it to the client with the corresponding error details.
To make error handling more uniform across HTTP and gRPC, you can use the standard error definitions provided by Google API Design. Here's a table showing the corresponding HTTP and gRPC error codes:
By using these standard error definitions, you can make error handling more consistent across HTTP and gRPC.
Assertion and Return Types
You can use the = operator to judge errors, which is a common pattern in Go. This involves defining a custom error type as an enum value and comparing it directly to determine the error type.
This approach is straightforward and easy to use, especially when dealing with a large number of error types. For example, you can use a switch-case statement to handle different error types.
It's worth noting that error types can be problematic, especially when they're used as part of a public API. This is because they can introduce tight coupling between the API and the caller, making the API more brittle.
Worth a look: Gcloud Api Using Golang
Assertion
Assertion is a crucial concept in programming that helps catch errors early on. It's like having a safety net that prevents your code from going haywire.
In programming, an assertion is a statement that checks if a certain condition is true. For example, in the article, we saw a function that checked if a list was empty before trying to access its elements.
Assertions can be used to validate user input, check for invalid states, and even test the integrity of data. They're a great way to ensure your code is working as expected.
In the article, we saw an example of an assertion being used to check if a value was within a certain range. This helped prevent the code from crashing due to an invalid input.
Assertions are typically used in development environments, where they can be enabled or disabled as needed. They're not meant to be used in production code, where they can slow down the application.
In some programming languages, assertions can be used to test the behavior of functions or methods. For example, in the article, we saw an example of an assertion being used to check the return type of a function.
Types
Types are an important aspect of error handling in Go, and understanding how to use them effectively can make a big difference in your code's maintainability and scalability.
A custom error type, like MyError, can be created to record file and line numbers, providing more context about what went wrong. This type can be used to get more information about the error.
Using custom error types can be beneficial, but it also introduces a problem of tight coupling between the error type and the caller. This can make the API more fragile and difficult to maintain.
Here's a simple way to avoid this problem: use custom error types sparingly, or at least avoid making them part of the public API. This will help keep your codebase more flexible and easier to work with.
In general, it's better to use sentinel errors and wrap them with more context, rather than creating custom error types. This approach is more straightforward and easier to understand.
Here are some key differences between using custom error types and sentinel errors:
Version-Specific Considerations
In Go 1.13 and later versions, two significant changes were made to improve error handling.
The first change added wrapping functionality to fmt.Errorf, allowing you to add custom error information while still providing the original error context.
The second change introduced the Is() and As() methods in the errors package, enabling you to check if an error is a specific type and extract its underlying cause.
To take advantage of these changes, it's essential to use Go's error wrapping pattern, which involves using fmt.Errorf() with %w to create wrapped errors. This approach allows you to add custom error information without losing the original error context.
By following this pattern and using errors.Is() and errors.As() consistently, you can ensure that your error handling is robust and easy to work with.
Here's an interesting read: How to Update a Github Using Golang
General Scheme and Optimization
When handling errors in Go, it's common to use a general scheme like the code-message pattern. This pattern involves using a code, which can be a number or a predefined string, to indicate the type of error. The code is usually used by the code itself to decide how to handle the error, while the message is for human consumption and can be displayed to the user.
The code is often used to indicate success (0) or failure (non-zero), making it easy to write simple error-handling code. However, this approach has its downsides. For instance, developers need to provide a wide range of error codes and corresponding messages, which can be time-consuming.
Here are some potential issues with the code-message pattern:
- Developers need to provide a comprehensive set of error codes and corresponding messages.
- It's unclear whether to return the underlying error or just the top-level error, especially if the underlying error contains sensitive information.
- Users may not understand the error messages, which can be confusing.
To optimize error handling, you can consider using a more robust error-handling mechanism, such as gRPC, which we'll explore in the next section.
Practice and Summary
In Go, it's essential to handle errors properly, and we've learned that using the error type is the most straightforward approach.
The best way to handle errors in Go is to return them as part of the function's result, as we saw in the example of the `divide` function.
In Go, it's a good practice to check for errors immediately after a function call, as demonstrated in the `main` function.
Error handling in Go is not just about checking for errors, but also about providing meaningful error messages, which can be achieved using the `fmt.Errorf` function.
The `defer` statement can be used to handle errors in a more elegant way, as we saw in the example of the `close` function.
By following these best practices, you can write more robust and maintainable code that handles errors effectively.
Curious to learn more? Check out: Golang vs Go
Standard Library and Elimination
In Go 1.13, the errors standard library was improved to simplify handling errors that contain other errors.
The errors package now allows you to implement the Unwrap method, which returns the underlying error. This makes it easier to unwrap and handle nested errors.
For example, you can use the Unwrap method to get the underlying error from an error that contains it. This is useful when you need to handle errors that are wrapped in another error.
With the new Is and As functions, you can check if an error is a specific type or if it contains a specific error. This makes it easier to handle errors in a more robust way.
Here are some key differences between the old and new ways of handling errors:
Note that while `fmt.Errorf` with `%w` is a new way to handle errors, it's recommended to use `pkg/errors` library's `Wrap` method instead, as it includes the stack context.
Standard Library

In Go 1.13, the standard library has introduced new features to simplify handling errors that contain other errors. This includes the ability to implement the Unwrap method on an error type, which allows you to return the underlying error.
The Unwrap method is useful for error types that wrap other errors, such as the QueryError type mentioned earlier. By implementing the Unwrap method, you can easily retrieve the underlying error and handle it accordingly.
The standard library also includes two new functions for checking errors: Is and As. These functions can be used to check if an error is of a specific type or if it contains another error.
For example, you can use the Is function to check if an error is of type QueryError, like this: if errors.Is(err, QueryError{}) { ... }
Alternatively, you can use the As function to assert that an error is of a specific type, like this: if e, ok := err.(QueryError); ok { ... }
The fmt.Errorf function has also been updated in Go 1.13 to support a new %w directive, which allows you to wrap an error with another error. This can be useful for adding additional information to an error without losing the underlying error.
For example, you can use %w to wrap an error like this: return fmt.Errorf("update Order failed: %w", err)
Note that %w does not include the stack context, and it's recommended to use the pkg/errors library's Wrap function instead for printing stack information.
Here's a summary of the new features in Go 1.13's standard library:
Eliminate Handling by Removing It
The Standard Library is a collection of pre-built functions that can be used to perform common tasks, like sorting and searching.
One of the main benefits of using the Standard Library is that it eliminates the need for manual handling of data, which can be time-consuming and prone to errors.
Manual handling of data can lead to bugs and inconsistencies, as seen in the example of the "sort" function being used to sort a list of integers.
Take a look at this: Define a Map of Custom Schema Data Type Golang

The Standard Library's "sort" function is a built-in function that can be used to sort lists of integers in ascending or descending order.
By using the Standard Library's "sort" function, developers can avoid writing their own sorting algorithms, which can be complex and difficult to debug.
For example, the "sort" function can be used to sort a list of integers in ascending order with a single line of code, making it much faster and more efficient than writing a custom sorting algorithm.
This can save developers a significant amount of time and effort, allowing them to focus on other aspects of their project.
Explore further: Golang Reflect to Call Function in Package
Featured Images: pexels.com


