Reputation: 403
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
Upvotes: 4
Views: 2353
Reputation: 1516
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
Upvotes: 1
Reputation:
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:
- 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
Upvotes: 9