为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

Go
qq_笑_17 2021-09-10 20:57:37
根据四十四十对这个问题的答复:fmt.Sprint(e)将调用e.Error()将值转换e为 a string。如果Error()方法调用fmt.Sprint(e),则程序将递归,直到内存不足。您可以通过将 the 转换为e没有StringorError方法的值 来中断递归。这对我来说仍然很困惑。为什么 fmt.Sprint(e) 调用 e.Error() 而不是 String()?我尝试使用 Stringer 接口,这是我的代码:package mainimport (  "fmt"  "math")type NegativeSqrt float64func (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 回答

?
慕码人8056858

TA贡献1803条经验 获得超6个赞

好像直接说明就是 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 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

这是因为在 Go 中类型 switch 的行为就像普通 switch 一样,所以case 的 order 很重要。


查看完整回答
反对 回复 2021-09-10
?
MMMHUHU

TA贡献1834条经验 获得超8个赞

因为类型是error,接口error是


  type error interface{

     Error() string

  }

每个人都error必须有一个Error() string方法,但不一定要有一个String() string方法。这就是为什么首先检查Error()方法的逻辑。


查看完整回答
反对 回复 2021-09-10
?
牛魔王的故事

TA贡献1830条经验 获得超3个赞

让我扩展 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部分,因为它被认为是一个非平凡的类型。

这将我们带到第 806 行并调用handleMethods()

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)


查看完整回答
反对 回复 2021-09-10
  • 3 回答
  • 0 关注
  • 296 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信