fmt.Sprint(e) 在 Error 方法中产生的无限循环

根据四十四十对这个问题的答复:


fmt.Sprint(e)将调用e.Error()将值转换e为 a string。如果Error()方法调用fmt.Sprint(e),则程序将递归,直到内存不足。


您可以通过将 the 转换为e没有StringorError方法的值 来中断递归。


这对我来说仍然很困惑。为什么 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))

}


qq_笑_17
浏览 297回答 3
3回答

慕码人8056858

好像直接说明就是 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()。这意味着第一个error.Error()被调用以生成字符串,然后再次处理并打印为字符串。这里是否error有方法String无关紧要。问题是为什么NegativeSqrt用一种方法而不是另一种方法打印。键入NegativeSqrt同时实现了fmt.Stringer与error接口,所以它是由执行fmt封装的接口应该被用来获取string来自NegativeSqrt(因为fmt.Sprint通过采用其参数interface{})。为了说明这一点,请考虑以下示例:package mainimport (&nbsp; &nbsp; "fmt")type NegativeSqrt float64func (e NegativeSqrt) Error() string {&nbsp; &nbsp; return ""}func (e NegativeSqrt) String() string {&nbsp; &nbsp; return ""}func check(val interface{}) {&nbsp; &nbsp; switch val.(type) {&nbsp; &nbsp; case fmt.Stringer:&nbsp; &nbsp; &nbsp; &nbsp; fmt.Println("It's stringer")&nbsp; &nbsp; case error:&nbsp; &nbsp; &nbsp; &nbsp; fmt.Println("It's error")&nbsp; &nbsp; }}func check2(val interface{}) {&nbsp; &nbsp; switch val.(type) {&nbsp; &nbsp; case error:&nbsp; &nbsp; &nbsp; &nbsp; fmt.Println("It's error")&nbsp; &nbsp; case fmt.Stringer:&nbsp; &nbsp; &nbsp; &nbsp; fmt.Println("It's stringer")&nbsp; &nbsp; }}func main() {&nbsp; &nbsp; var v NegativeSqrt&nbsp; &nbsp; check(v)&nbsp; &nbsp; check2(v)}执行这个给出:% go run a.goIt's stringerIt's error这是因为在 Go 中类型 switch 的行为就像普通 switch 一样,所以case 的 order 很重要。

MMMHUHU

因为类型是error,接口error是&nbsp; type error interface{&nbsp; &nbsp; &nbsp;Error() string&nbsp; }每个人都error必须有一个Error() string方法,但不一定要有一个String() string方法。这就是为什么首先检查Error()方法的逻辑。

牛魔王的故事

让我扩展 tumdum 的发现,以便更清楚。我将从一个呼叫跳转到另一个呼叫以展示我们如何进入循环。我们从练习开始func&nbsp;(e&nbsp;NegativeSqrt)&nbsp;Error()&nbsp;string&nbsp;{ &nbsp;&nbsp;fmt.Printf(".")&nbsp;&nbsp;return&nbsp;fmt.Sprint(e)}这将我们带到fmt/print.go的第 237 行:func&nbsp;Sprint(a&nbsp;...interface{})&nbsp;string在函数内部,我们的下一个跳转在第 239 行:p.doPrint(a,&nbsp;false,&nbsp;false)我们到达第 1261 行:func&nbsp;(p&nbsp;*pp)&nbsp;doPrint(a&nbsp;[]interface{},&nbsp;addspace,&nbsp;addnewline&nbsp;bool)&nbsp;{在该函数中,我们将使用我们的error参数跳转到第 1273 行:prevString&nbsp;=&nbsp;p.printArg(arg,&nbsp;'v',&nbsp;0)我们在第 738 行到达了一个巨大的核心怪物函数:func&nbsp;(p&nbsp;*pp)&nbsp;printArg(arg&nbsp;interface{},&nbsp;verb&nbsp;rune,&nbsp;depth&nbsp;int)&nbsp;(wasString&nbsp;bool)&nbsp;{在里面,你可以看到一个大switch case开关。error进入该default部分,因为它被认为是一个非平凡的类型。这将我们带到第 806 行并调用handleMethods():if&nbsp;handled&nbsp;:=&nbsp;p.handleMethods(verb,&nbsp;depth);&nbsp;handled&nbsp;{我们到达第 688 行:func&nbsp;(p&nbsp;*pp)&nbsp;handleMethods(verb&nbsp;rune,&nbsp;depth&nbsp;int)&nbsp;(handled&nbsp;bool)&nbsp;{在该函数内部,在第 724 行,调用Error()发生,从而完成循环:p.printArg(v.Error(),&nbsp;verb,&nbsp;depth)
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go