Stringer : runtime: goroutine stack exceeds 1000000000-byte limit
jerome-laforge opened this issue · comments
Hello,
I want to use go-spew into String() string.
For example:
type Example struct {
foo string
}
func (e Example) String() string {
return spew.Sprintf("%+v", e)
}
Unfortunately, that generates:
runtime: goroutine stack exceeds 1000000000-byte limit
fatal error: stack overflow
runtime stack:
runtime.throw(0x55c280, 0xe)
/usr/lib/go/src/runtime/panic.go:547 +0x90
runtime.newstack()
/usr/lib/go/src/runtime/stack.go:940 +0xb11
runtime.morestack()
/usr/lib/go/src/runtime/asm_amd64.s:359 +0x7f
goroutine 1 [stack growth]:
fmt.(*pp).argNumber(0xc826df4820, 0x0, 0x553da0, 0x3, 0x2, 0x1, 0x0, 0x0, 0x0)
/usr/lib/go/src/fmt/print.go:1088 fp=0xc8401002b8 sp=0xc8401002b0
fmt.(*pp).doPrintf(0xc826df4820, 0x553da0, 0x3, 0xc826dbf530, 0x1, 0x1)
/usr/lib/go/src/fmt/print.go:1144 +0xc87 fp=0xc840100640 sp=0xc8401002b8
fmt.Sprintf(0x553da0, 0x3, 0xc826dbf530, 0x1, 0x1, 0x0, 0x0)
/usr/lib/go/src/fmt/print.go:203 +0x6f fp=0xc840100690 sp=0xc840100640
github.com/davecgh/go-spew/spew.Sprintf(0x553da0, 0x3, 0xc840100730, 0x1, 0x1, 0x0, 0x0)
.../github.com/davecgh/go-spew/spew/spew.go:126 +0x90 fp=0xc8401006e8 sp=0xc840100690
main.Person.String(0x26, 0x5544e0, 0x6, 0x554540, 0x7, 0xc82000e0c0, 0x3, 0x3, 0x0, 0x0)
.../src/cmd/spew/main.go:17
Do you know a elegant manner to do this ?
Thx
That causes infinite recursion in the exact same way as the following would:
type Example struct {
foo string
}
func (e Example) String() string {
return fmt.Sprintf("%v", e)
}
I'd recommend not using spew
in a stringer like this. The elegant way to handle it is to simply move the call to spew
outside of the stringer to the caller like so:
package main
import "github.com/davecgh/go-spew/spew"
type Example struct {
foo string
}
func main() {
e := Example{"foostring"}
spew.Printf("%+v\n", e)
}
Output:
{foo:foostring}
ok, thx again for this confirmation
I know this is a user error and should not be supported by spew, but I wonder if it's possible and a good idea for spew to detect when it's indirectly calling itself a second time, and print a more helpful error message?
Or is the overhead of doing that too much (in terms of code to maintain and run-time performance) to outweigh the benefit?
@shurcooL: That's not a bad idea, but I'm really not sure if that could be done without having undesirable side effects.
spew does detect recursion within in a single call by keeping track of the pointers in a local map that is part of the instance, but the issue here is it's effectively calling the upper-level print routine over and over.
Adding some type of global tracking to detect this would mean that code such as the following wouldn't work:
e := &Example{"foostring"}
spew.Println(foo)
e.foo = "Something new"
spew.Println(foo) // <--- spew would now filter this because it would be in the global already printed map
I don't have it completely figured out, it was just a thought.
I was imagining something that would look at the stack trace whenever a top-level spew function is called, to check that it's not being called a second time (from spew).
It may very well not be feasible; I just wanted us to consider it.
I wonder if it's possible and a good idea for spew to detect when it's indirectly calling itself a second time, and print a more helpful error message?
If this modification is possible, is it applicable to print directly the structure as spew usually rather than to display this helpful error message?
Or let to user by configuration the choice to display helpful error or print the structure directly without error.