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

时间:2015-01-13 18:14:11

标签: go

根据fortyforty对this question的回复:

  

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

     

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

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

3 个答案:

答案 0 :(得分:13)

似乎有人解释directly是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.Stringererror接口,因此应该使用fmt包的实现来使用哪个接口从{{1}获取string ()fmt.Sprint将参数设为NegativeSqrt)。

为了说明这一点,请考虑以下示例:

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)
}

这是因为在Go类型中,开关的行为与普通开关一样,所以order of cases matters

答案 1 :(得分:3)

因为类型为errorerror的界面为

  type error interface{
     Error() string
  }

每个error必须使用Error() string方法,但不必使用String() string方法。这就是为什么首先检查Error()方法的逻辑。

答案 2 :(得分:0)

让我扩大tumdum的发现,以便更清晰。

我会从通话电话跳到显示我们如何进入循环。

我们从练习开始

func (e NegativeSqrt) Error() string {
  fmt.Printf(".")
  return fmt.Sprint(e)
}

它将我们带到fmt/print.go的第237行:

func Sprint(a ...interface{}) string

在函数内部,我们的下一个跳转是在第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) {

在其中,你可以看到一个很大的switch case开关。 error位于default部分,因为它被视为非平凡类型。

通过调用handleMethods()

,我们将其转到806行
if handled := p.handleMethods(verb, depth); handled {

我们到达第688行:

func (p *pp) handleMethods(verb rune, depth int) (handled bool) {

在该函数内部,在第724行,调用Error(),这完成了循环:

p.printArg(v.Error(), verb, depth)