根据fortyforty对这个问题的回复:
fmt.Sprint(e)将调用e.Error()将值转换e为astring.如果Error()方法调用fmt.Sprint(e),则程序会递归直到内存不足.您可以通过将
e值转换为不带String或Error方法的值 来中断递归.
这对我来说仍然令人困惑.为什么fmt.Sprint(e)调用e.Error()而不是String()?我尝试使用Stringer接口,这是我的代码:
package main
import (
"fmt"
"math"
)
type NegativeSqrt float64
func (e NegativeSqrt) Error() string {
fmt.Printf(".")
return fmt.Sprint(e)
}
func (e NegativeSqrt) String() string {
return fmt.Sprintf("%f", e)
}
func Sqrt(x float64) (float64, error) {
if x < 0 {
return 0, NegativeSqrt(x)
}
return math.Sqrt(x), nil
}
func main() {
fmt.Println(Sqrt(2))
fmt.Println(Sqrt(-2))
}
Run Code Online (Sandbox Code Playgroud)
tum*_*dum 14
它似乎直接解释了fmt包的来源:
// Is it an error or Stringer?
// The duplication in the bodies is necessary:
// setting handled and deferring catchPanic
// must happen before calling the method.
Run Code Online (Sandbox Code Playgroud)
这意味着首先调用error.Error()来生成字符串,该字符串再次处理并打印为字符串.
是否error有方法String在这里无关紧要.问题是为什么NegativeSqrt用一种方法而不是另一种方法打印.类型NegativeSqrt同时实现了fmt.Stringer与error接口,所以它是由执行fmt封装的接口应该被用来获取string来自NegativeSqrt(因为fmt.Sprint通过采用其参数interface{}).
为了说明这个,请考虑以下示例
package main
import (
"fmt"
)
type NegativeSqrt float64
func (e NegativeSqrt) Error() string {
return ""
}
func (e NegativeSqrt) String() string {
return ""
}
func check(val interface{}) {
switch val.(type) {
case fmt.Stringer:
fmt.Println("It's stringer")
case error:
fmt.Println("It's error")
}
}
func check2(val interface{}) {
switch val.(type) {
case error:
fmt.Println("It's error")
case fmt.Stringer:
fmt.Println("It's stringer")
}
}
func main() {
var v NegativeSqrt
check(v)
check2(v)
}
Run Code Online (Sandbox Code Playgroud)
执行此操作会给出:
% go run a.go
It's stringer
It's error
Run Code Online (Sandbox Code Playgroud)
这是因为在Go类型中,开关的行为与普通开关一样,因此情况顺序很重要.
因为类型是error,接口error是
type error interface{
Error() string
}
Run Code Online (Sandbox Code Playgroud)
每个人都error必须有一个Error() string方法,但不一定要有一个String() string方法。这就是为什么首先检查Error()方法的逻辑。