flag: Stack overflow when return stringer with fmt.Sprint

181 views
Skip to first unread message

Muhammad Shulhan

unread,
Sep 1, 2016, 4:59:12 AM9/1/16
to golang-nuts
Dear all,

I will describe the problem step by step, please bear with me.

Lets take a look at first example ,

package main

import (
 "fmt"
)

type Slices []string

func (sl *Slices) String() string {
 // OK
 return fmt.Sprint(sl)
}

func main() {
 var slices Slices

 fmt.Print("slices:", slices)
}


Run: https://play.golang.org/p/Cjb0xCVaCC
Output:

slices:[]

Let change String() return to fmt.Sprint(*sl).

Run: https://play.golang.org/p/St6TyltA56
Output:

slices:[]

Using the above prototype lets build a flag that parse Slices. Here is the code that return without stack overflow,

package main

import (
"flag"
"fmt"
)

type Slices []string

func (sl *Slices) String() string {
return fmt.Sprint(*sl)
}

func (sl *Slices) Set(vv string) error {
// STUB for clarity
return nil
}

func main() {
var sl Slices
flag.Var(&sl, "slices", "Test parsing slice flag.")
flag.Parse()

fmt.Printf("slices: %s\n", sl)
}

Run: https://play.golang.org/p/Sol4C8hRk7
Output:

slices: []

Last one, lets change the String return to "fmt.Sprint(sl)".

Run: https://play.golang.org/p/550Krvim77
Output:

runtime: goroutine stack exceeds 250000000-byte limit
fatal error: stack overflow
 
Question: is this a bug? If it is I would report one.

PS:

Previous discussion that I found mentioning the same problem:

* https://groups.google.com/forum/#!searchin/golang-nuts/fmt.Sprint|sort:relevance/golang-nuts/jJ72z0Ssahc/FDwArwNCiw4J

dja...@gmail.com

unread,
Sep 1, 2016, 5:25:24 AM9/1/16
to golang-nuts
Hi,
Do you know what infinite recursion is ?
fmt.String use your String method

see https://play.golang.org/p/jeOilZW7JU

dja...@gmail.com

unread,
Sep 1, 2016, 5:28:23 AM9/1/16
to golang-nuts, dja...@gmail.com
s/fmt.String/fmt.Sprintf/

Muhammad Shulhan

unread,
Sep 1, 2016, 6:02:04 AM9/1/16
to golang-nuts, dja...@gmail.com

On Thursday, 1 September 2016 16:25:24 UTC+7, dja...@gmail.com wrote:
Hi,
Do you know what infinite recursion is ?

Yes, I know.
 
fmt.String use your String method

see https://play.golang.org/p/jeOilZW7JU


By that logic, the first example should be stack overflow too, but it's not.

Uli Kunitz

unread,
Sep 1, 2016, 9:32:26 AM9/1/16
to golang-nuts, dja...@gmail.com
fmt.Sprint(sl) is not the same as fmt.Sprint(*sl). That makes the String methods in both examples different.

Harald Weidner

unread,
Sep 1, 2016, 3:11:51 PM9/1/16
to golang-nuts
Hello,

On Thu, Sep 01, 2016 at 01:59:12AM -0700, Muhammad Shulhan wrote:

> > type Slices []string
> >
> > func (sl *Slices) String() string {
> > return fmt.Sprint(sl)
> > }

> runtime: goroutine stack exceeds 250000000-byte limit
> fatal error: stack overflow

It is always dangerous to call fmt.Sprint() (or any other print method of
the fmt package) from a String() method of a value that implements the
Stringer interface. fmt.Sprint() might call the String() method itselfes,
which leads to infinite recursion.

From the fmt documentation (https://golang.org/pkg/fmt/#pkg-overview):

| [...]
| 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).

Wether this happens or not depends on the concrete execution path of
fmt.Sprint(). Even if three of your four exampes work, I guess they could
fail with future implementations of the fmt package.

You should always pass basic types to fmt.Sprint(), e.g.

func (sl *Slices) String() string {
return fmt.Sprint([]string(*sl))
}

Again, from the fmt documentation:

| 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.

Harald

Mhd Sulhan

unread,
Sep 2, 2016, 6:23:28 AM9/2/16
to Harald Weidner, golang-nuts
Pada Thu, 1 Sep 2016 21:10:44 +0200
Harald Weidner <hweidne...@gmx.net> menulis:
Ah, I see. Thank you Harald, for pointing and explaining the cause. I
must learn to RTFM thoroughly. My mistake.
Reply all
Reply to author
Forward
0 new messages