Go language advanced: deep understanding of custom error interface and infinite recursive trap
In Go language, errors are not an exception, but a value. By implementing the Error interface, we can create custom error types with rich context information. This article will combine Go Tour’s ‘exercise: errors’ exercise to explore how to implement a square root function that supports negative number detection, and reveal the ‘infinite recursion’ pit that is easy to step on.
1. Analysis of Core Needs
We need to complete the following tasks:
* Define a custom error type ErrneGativeSqrt.
* Implement the Error() String method for this type to satisfy the Error interface.
* Modify the sqrt function, which returns the custom error when the input is negative.
The implementation of code
Define custom error types
We first define a new type based on `float64`:
type ErrNegativeSqrt float64
Implement the Error interface
This is the most critical part of this exercise. We need to make ErrorNegativeSqrt implement the Error interface:
func (e ErrNegativeSqrt) Error() string {
return fmt.Sprintf("cannot Sqrt negative number: %v", float64(e))
}
> ⚠️ Guide to Pits: Why do you have to convert `float64(e)`?
> If you write `fmt.sprint(e)` directly in the error() method, the program will fall into infinite recursion.
> Cause: `fmt.sprint` When printing a variable, if it is found that the variable implements the error interface, it will call its error() method first.
> Result: Error() calls `fmt.sprint` -> `fmt.sprint` to call Error() -> an infinite loop.
> Solution: Break this call chain with `float64(e)` converting the type to the base type.
Perfect sqrt function
Use the standard library `math.sqrt` and add the error checking logic:
package main
import (
"fmt"
"math"
)
// ErrNegativeSqrt 自定义错误类型
type ErrNegativeSqrt float64
// Error 实现 error 接口
// 注意:必须将 e 转换为 float64(e),否则 fmt.Sprint(e) 会再次调用 Error() 方法,导致无限递归
func (e ErrNegativeSqrt) Error() string {
return fmt.Sprintf("cannot Sqrt negative number: %v", float64(e))
}
// Sqrt 计算平方根,如果输入为负数则返回错误
func Sqrt(x float64) (float64, error) {
if x < 0 {
return 0, ErrNegativeSqrt(x)
}
return math.Sqrt(x), nil
}
func main() {
fmt.Println(Sqrt(2))
fmt.Println(Sqrt(-2))
}
3. Run the result After executing the main function, the output is as follows:
/app/go-tour/exercise-errors # go run main.go
1.4142135623730951 <nil>
0 cannot Sqrt negative number: -2
4. Summary
Through this simple exercise, we learned:
1. Implicit implementation of the interface: As long as the error() String method is defined, any type can be an error.
2. Error is data: we can carry additional fields (such as the value in this example) in the error like the ordinary structure.
3. Formatting details: When using the `fmt` package to implement the type of its own interface, you need to pay attention to type conversion to avoid recursion.