英文:
struct String() implemention causes stack overflow with Sprintf "+" flag
问题
Golang新手问题:为什么我不能在String()方法的实现中使用"%+v"标志来打印结构体?
我有一个结构体,我想实现一个String()
方法来进行漂亮的打印。我喜欢这里给出的答案,但是我不喜欢打字,所以我试图修改它,以便在结构体中使用"%+v"格式标志返回一个字符串。根据fmt文档:
%v 默认格式的值 当打印结构体时,加号标志(%+v)会添加字段名
如果我简单地使用fmt.Printf("%+v", color)
调用它,这样是可以的,但是如果我尝试在String()方法的实现中加入"+"标志,就会导致堆栈溢出(这是我第一次在stackoverflow.com上提出"stack overflow"问题)。
我确定我在这里没有理解指针引用,或者存在一些递归。我怀疑这个新手找到了我的第一个Golang bug,所以有人可以解释一下吗?
在这里可以看到go play的演示:https://play.golang.org/p/13_qI8Iwwa
英文:
Golang noob question: Why can I not use the "%+v"
flag for a struct in the String() implementation method?
I have a struct where I want to implement a String()
method for pretty print. I like the answer given here, but I don't like to type, so I'm trying to modify it to return a string using the "%+v"
format flag for structs. from the fmt doc:
> %v the value in a default format when printing structs, the plus flag
> (%+v) adds field names
This works fine if I simply call it with fmt.Printf("%+v", color)
, but if I try to put the +
flag in the String()
implementation, I get a stack overflow (my first chance to ask a "stack overflow" question on stackoverflow.com )
I'm sure I'm not understanding a pointer reference here, or there is some recursion. I doubt this noob found my first Golang bug, so can someone please explain?
see go play demonstration here https://play.golang.org/p/13_qI8Iwwa
答案1
得分: 9
请注意,我将为您翻译以下内容:
除非使用
%T
和%p
这两个动词打印,否则对于实现了特定接口的操作数,会应用特殊的格式化考虑。应用顺序如下:
- 如果操作数实现了
String() string
方法,该方法将被调用以将对象转换为字符串,然后按照所需的动词格式化。
为了避免出现递归,例如:
type X string func (x X) String() string { return Sprintf("<%s>", x) }
在递归之前将值转换:
func (x X) String() string { return Sprintf("<%s>", string(x)) }
如果该类型具有String方法,还可以通过自引用数据结构(例如包含自身作为元素的切片)触发无限递归。然而,这种病态情况很少见,且该包不会对其进行保护。
在代码中:
func (c Color) String() string {
// 这会导致堆栈溢出
return fmt.Sprint(c)
}
对fmt.Sprint(c)
或fmt.Println(c)
的调用会再次递归调用func (c Color) String() string
,导致溢出:您可以在Go Playground上尝试。
此外,这个示例可以正常工作:https://play.golang.org/p/NYLtrxUeiA
英文:
See Package fmt Docs:
> Except when printed using the verbs %T
and %p
, special formatting
> considerations apply for operands that implement certain interfaces.
> In order of application:
>
> 5. If an operand implements method String() string
, that method will be invoked to convert the object to a string, which will then be
> formatted as required by the verb (if any).
>
> To avoid recursion in cases such as
>
> type X string
> func (x X) String() string { return Sprintf("<%s>", x) }
> convert the value before recurring:
>
> func (x X) String() string { return Sprintf("<%s>", string(x)) }
> Infinite recursion can also be triggered by self-referential data
> structures, such as a slice that contains itself as an element, if
> that type has a String method. Such pathologies are rare, however, and
> the package does not protect against them.
Inside:
func (c Color) String() string {
// THIS CAUSES STACK OVERFLOW
return fmt.Sprint(c)
}
The call to
fmt.Sprint(c)
or fmt.Println(c)
which calls func (c Color) String() string
again recursively causes overflow: try it on The Go Playground
Also this works fine: https://play.golang.org/p/NYLtrxUeiA
答案2
得分: 0
你可以通过将类型Color
重新定义为一个新的局部类型(例如type _Color Color
),然后将Sprintf
应用于转换后的值_Color(color)
,从而避免无限递归调用你的String()
函数。
func (color Color) String() string {
type _Color Color
return fmt.Sprintf("%+v", _Color(color))
}
注意:不要使用类型别名(例如type _Color = Color
),因为这不会引入新的类型,而只是为旧类型引入一个新名称,这样你就会回到无限递归的问题。
编辑:我已经相应地更新了你的示例,你可以检查它是否正常工作:https://go.dev/play/p/Cb1jQTcajc4
英文:
You can get around the infinite recursion by redefining your type Color
as a new local type (e.g. type _Color Color
) and then applying Sprintf
to the converted value _Color(color)
, since that cannot recursively call your String()
function.
func (color Color) String() string {
type _Color Color
return fmt.Sprintf("%+v", _Color(color))
}
Note: don't use type aliases (e.g. type _Color = Color
) because that doesn't introduce a new type, but just introduces a new name for the old type and you're back to that infinite recursion problem.
EDIT: I updated your example accordingly, so you can check that it works: https://go.dev/play/p/Cb1jQTcajc4
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论