我正在阅读 Andrew Gerrand 的这篇文章,作者提到,因为错误是一个接口
您可以使用任意数据结构作为错误值,以允许调用者检查错误的详细信息。
并给出这个例子
type NegativeSqrtError float64
func (f NegativeSqrtError) Error() string {
return fmt.Sprintf("math: square root of negative number %g", float64(f))
}
但没有真正展示它是如何实现的,只是讨论它在类型断言中的可能用途。尽管我想象您可以在发生这种情况时简单地返回一个值,而不是使用
fmt.Errorf
,如下例所示
package main
//trying to define a custom error type in Go
import "fmt"
type NegativeError float64
func (f NegativeError) Error() string {
return fmt.Sprintf("%v: temperature can't go below absolute zero", f)
}
func compute(a, b float64) (float64, error) {
var t float64
t = a - b
if t < 0 {
return t, NegativeError(t)
}
return t, nil
}
func main() {
fmt.Println(compute(4, 5))
}
但这不起作用并导致以下错误
runtime: goroutine stack exceeds 1000000000-byte limit
runtime: sp=0xc0200e13a0 stack=[0xc0200e0000, 0xc0400e0000]
fatal error: stack overflow
这似乎只有在您实现
error
接口的 Error()
方法时才会出现问题,但当您更改方法名称时它就会起作用。 (如下图)
package main
//trying to define a custom error type in Go
import "fmt"
type NegativeError float64
func (f NegativeError) Err() string {
return fmt.Sprintf("%v: temperature can't go below absolute zero", f)
}
func compute(a, b float64) (float64, string) {
var t float64
t = a - b
if t < 0 {
return t, NegativeError(t).Err()
}
return t, ""
}
func main() {
fmt.Println(compute(4, 5))
}
或者当您在结构类型而不是浮点数上实现
error
接口时。 (如下图)
package main
//trying to define a custom error type in Go
import (
"fmt"
)
type NegativeError struct {
value float64
}
func (f NegativeError) Error() string {
return fmt.Sprintf("%v: temperature can't go below absolute zero", f.value)
}
func compute(a, b float64) (float64, error) {
var t float64
t = a - b
if t < 0 {
return t, NegativeError{t}
}
return t, nil
}
func main() {
fmt.Println(compute(4, 5))
}
明显的简单修复方法是在结构类型上实现接口,但我想知道是否有其他人遇到过这个特定的错误以及他们如何处理它,或者这是否是错误的解决方法。谢谢。
正如评论中已经提到的,如果将错误类型本身传递给
Error()
,则 fmt.Sprintf
方法会调用自身,因为它用于将错误类型格式化为字符串。
有多种方法可以解决此问题:
f
转换为基础 float64
类型并将其传递给 fmt.Sprintf
:func (f NegativeError) Error() string {
return fmt.Sprintf("%v: temperature can't go below absolute zero", float64(f))
}
%v
,而应使用 %f
。不同之处在于 %v
将尝试对值进行字符串化,对于 error
类型,这意味着调用 Error() string
方法。 %f
会将 f
视为 float64
,因此它可以工作:func (f NegativeError) Error() string {
return fmt.Sprintf("%f: temperature can't go below absolute zero", f)
}