Be careful about printing error as string in GoLang

  sonic0002        2019-01-23 09:17:15       22,021        2    

In GoLang, we can format and produce string using fmt.Printf(), just like C, GoLang also supports format verbs like %s, %d which can be placeholder for different types of values. But please pay attention when printing error as string so that you will not fall into some trap.

Let's first take an example code snippet and see what trap we are talking about.

package main

import "fmt"

type A string

func (a A) Error() string {
	return fmt.Sprintf("%s is an error", a)
}

func main() {
	a := A("hello")
	fmt.Printf("error is %s", a)
}

What do you expect the output is? Do you expect "error is hello is an error"? Unfortunately there will be a stack overflow error after the program runs sometime. 

runtime: goroutine stack exceeds 1000000000-byte limit
fatal error: stack overflow

runtime stack:
runtime.throw(0x4c2869, 0xe)
        C:/Go/src/runtime/panic.go:608 +0x79
runtime.newstack()
        C:/Go/src/runtime/stack.go:1008 +0x737
runtime.morestack()
        C:/Go/src/runtime/asm_amd64.s:429 +0x97

The reason for the stack overflow is that when trying to format the object a into a string, it will call the Error() method of type A. Since A implements the Error() method, it is considered as an error type. When converting error to string using %s, it will call its Error() method.

In above code, the Error() method has the %s verb to convert a into a string, this in turn will call its Error() again, so this will cause it to call itself indefinitely and then stack overflows after some time.

To mitigate the error, need to explicitly convert the object a into string by calling string(a). 

package main

import "fmt"

type A string

func (a A) Error() string {
	return fmt.Sprintf("%s is an error", string(a))
}

func main() {
	a := A("hello")
	fmt.Printf("error is %s", a)
}

Now the output will be expected. A more obvious and frequent seen issue is when some data type implements the Stringer interface. 

package main

import "fmt"

type I int

func (i I) String() string {
	return fmt.Sprintf("%s is a string", i)
}

func main() {
	a := I(1)
	fmt.Printf("a is %s", a)
}

This will also produce stack overflow error. Actually the go vet tool would detect such recursive calls and will give you warnings.

The takeaway here is to be careful when implementing interface methods.

STACKOVERFLOW  GOLANG  FMT 

       

  RELATED


  2 COMMENTS


Anonym [Reply]@ 2019-01-24 13:52:43

In the printf line just use a.Error() 

Ke Pi [Reply]@ 2019-01-25 07:00:00

no, it does not help



  RANDOM FUN

Full stack