Interface signature conflicts with internal library's interface will lead to stack overflow

The follow sample code will lead to stack overflow because it has the same interface signature as encoding/json/Marshaler.

The json.Marshal(s) will call MarshalJSON internally. So, as a result it turns out to be a recursive function call.

Is that by design? Or a design mistake?

package main

import (
	"encoding/json"
	"fmt"
)

type IJSON interface {
	MarshalJSON() ([]byte, error)  // This interface signature is the same as "encoding/json/Marshaler"
}

type Student struct {
	Name string `json:"name"`
	Age  int    `json:"age"`
}

func (s *Student) MarshalJSON() ([]byte, error) {
	return json.Marshal(s)
}

func main() {
	s := &Student{"James", 12}

	data, err := s.MarshalJSON()
	if err != nil {
		panic(err)
	}

	fmt.Printf("%s", data)
}
2 Likes

MarshalJSON is used by types that want to overwrite the default behaviour. This works perfectly:

package main

import (
	"encoding/json"
	"fmt"
)

type Student struct {
	Name string `json:"name"`
	Age  int    `json:"age"`
}

func main() {
	if data, err := json.Marshal(Student{"James", 12}); err != nil {
		panic(err)
	} else {
		fmt.Printf("%s\n", data)
	}
}
2 Likes

Hi Charles,

Creating “IJSON” is a design mistake :slight_smile:

Your Interface collide with a public method of “encoding/json/Marshaler”
Internally encode/json uses reflexion to determinate what encoder to use.

By declaring a method func (s *Student) MarshalJSON() ([]byte, error) your Student becomes compliant with the json.Marshaler interface.

So the encoder uses your MarshalJSON() that turns out to become an infinite recursive call.

Go Interface system is permissive.

Best regards,

2 Likes

This is just an example that somebody may accidentally collide with third-party/stdlib interface. Just want to know if there’s way to avoid that.

2 Likes

It is a very special example that occurs because the encoder uses reflexion. It is not generalizable.

Note that the encoding/json package is not the stdlib.

In go we use packages to prevent collisions.

Remember that Go interfaces are implemented implicitly. There is neither explicit declaration nor “implements” keyword.

2 Likes

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.