Go 在错误方法中由fmt.Sprint(e)产生的无限循环
根据fortyforty对以下内容的回复:Go 在错误方法中由fmt.Sprint(e)产生的无限循环,go,Go,根据fortyforty对以下内容的回复: fmt.Sprint(e)将调用e.Error() 字符串。如果Error()方法调用fmt.Sprint(e),则 程序递归,直到内存不足 您可以通过将e转换为不带 字符串或错误方法 这仍然让我感到困惑。为什么fmt.Sprint(e)调用e.Error()而不是String()?我尝试使用Stringer接口,这是我的代码: package main import ( "fmt" "math" ) type NegativeSqrt f
fmt.Sprint(e)
将调用e.Error()
字符串
。如果Error()
方法调用fmt.Sprint(e)
,则
程序递归,直到内存不足
您可以通过将e
转换为不带
字符串
或错误
方法
这仍然让我感到困惑。为什么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))
}
主程序包
进口(
“fmt”
“数学”
)
类型NegativeSqrt float64
func(e NegativeSqrt)错误()字符串{
格式打印F(“.”)
返回fmt.Sprint(e)
}
func(e NegativeSqrt)String()字符串{
返回fmt.Sprintf(“%f”,e)
}
func Sqrt(x float64)(float64,错误){
如果x<0{
返回0,负数qrt(x)
}
返回math.Sqrt(x),无
}
func main(){
格式打印(Sqrt(2))
格式打印(Sqrt(-2))
}
似乎解释了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.
而不是被称为
它的意思是调用第一个来生成字符串,然后再次处理该字符串并将其打印为字符串
error
是否有方法String
在这里是无关的。问题是为什么负片qrt
是用一种方法而不是另一种方法打印的。TypeNegativeSqrt
实现了fmt.Stringer
和error
接口,因此取决于fmt
包的实现,应该使用哪个接口从NegativeSqrt
获取字符串
(因为它的参数是通过接口{}
)
为了说明这一点,请考虑这个例子:
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)
}
执行此命令将提供:
% go run a.go
It's stringer
It's error
这是因为in-Go类型开关的行为与普通开关类似,因此。因为类型是error
,而error
的接口是
type error interface{
Error() string
}
每个error
都必须有error()string
方法,但不必有string()string
方法。这就是为什么首先检查Error()
方法的逻辑 为了更清晰,让我扩展一下tumdum的发现
我将从一个呼叫跳到另一个呼叫,展示我们如何进入循环
我们从练习的开始
func (e NegativeSqrt) Error() string {
fmt.Printf(".")
return fmt.Sprint(e)
}
这将我们带到第237行:
在函数内部,我们的下一个跳转位于第239行:
p.doPrint(a, false, false)
我们到达1261号线:
func (p *pp) doPrint(a []interface{}, addspace, addnewline bool) {
在该函数中,我们将使用error
参数跳转到第1273行:
prevString = p.printArg(arg, 'v', 0)
我们在738行实现了一个巨大的核心怪物功能:
func (p *pp) printArg(arg interface{}, verb rune, depth int) (wasString bool) {
在里面,你可以看到一个大的开关盒
开关<代码>错误
出现在默认
部分,因为它被认为是一种非平凡类型
通过调用handleMethods()
,我们将看到第806行:
我们到达688号线:
func (p *pp) handleMethods(verb rune, depth int) (handled bool) {
在该函数内部,第724行调用Error()
,完成循环:
p.printArg(v.Error(), verb, depth)
Tomasz Kłak的回答也很精彩,但这一个解释了为什么它是以这种方式实现的。我不认为这个问题是关于主题/正确的。在你的“编辑”之后,这个回答现在解释了为什么我们有这种行为,谢谢。编辑:我修复了链接。给定的链接不起作用。通过指向提交而不是主提交来提供链接,它可以更改。
func (p *pp) handleMethods(verb rune, depth int) (handled bool) {
p.printArg(v.Error(), verb, depth)