【问题标题】:Why does adding an Error() method change program behavior in Go?为什么添加 Error() 方法会改变 Go 中的程序行为?
【发布时间】:2022-01-27 15:19:53
【问题描述】:

下面的程序打印“2.5”

package main

import (
    "fmt"
)

type myFloat64 float64

// func (f myFloat64) Error() string {
//     return fmt.Sprintf("Error with %v", f)
// }

func main() {
    var a myFloat64 = myFloat64(2.5)
    fmt.Printf("%v\n", a)
}

有趣的是,当我取消注释 Error() 方法时,程序不再打印“2.5”

问题: 为什么添加Error() 方法会改变程序行为?指向解释此行为的 Go 语言规范的指针将不胜感激。

【问题讨论】:

  • 我从一个至少部分多余的答案开始(因此关闭了问题),但请查看this version on the Go playground。请注意,我将fmt.Sprintf 更改为使用%f;如果您将%v 留在此处并运行它,您将看到无限递归。
  • 因为 %v 调用 Error 再次使用 %v 来格式化调用 Error 的值,而再次使用 %v 格式化值又调用 Error .... 你得到了一个无限循环,它产生了一个 SO .
  • 我觉得这个问题应该向其他提出相同问题的人开放。答案,OP,是因为当你将 Error 方法添加到你的浮点数时,你正在创建一个错误类型。您可以在此处查看规范:go.dev/ref/spec#Errors 然后,当您调用 Printf 时,代码会在打印期间调用该方法,在此处定义:cs.opensource.google/go/go/+/refs/tags/go1.17.5:src/fmt/…
  • @Jorge,是的,你的把戏有效,但主要问题是 %v。这是一个方便的工具来格式化你不知道它是什么的东西。但如果你知道它是什么:使用适合你的类型的动词。
  • 我希望 Go 操场告诉用户堆栈溢出。调试会更容易。

标签: go


【解决方案1】:

myFloat64 实现了error 接口:

type error interface {
  Error() string
}

fmt.Println() 会将a 视为error 值,并通过调用a.Error() 打印错误消息,它执行fmt.Sprintf("Error with %v", f)。但是Sprintf 的行为与Println 一样,它还将f 视为error 值并调用Error()。此递归无限循环并导致堆栈溢出。

【讨论】:

猜你喜欢
  • 1970-01-01
  • 2021-09-03
  • 1970-01-01
  • 1970-01-01
  • 2014-04-16
  • 1970-01-01
  • 2023-03-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多