Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

encoding/gob: permit registering duplicate names #36345

Open
dsnet opened this issue Jan 1, 2020 · 1 comment
Open

encoding/gob: permit registering duplicate names #36345

dsnet opened this issue Jan 1, 2020 · 1 comment

Comments

@dsnet
Copy link
Member

@dsnet dsnet commented Jan 1, 2020

In go1.9, type aliases were introduced. The purpose of type aliases is to permit the movement of types from one location to another. This implies:

  1. that the real name of the type can change over time
  2. that there can be multiple names for the exact same Go type

The use of type aliases interacts poorly with gob-encoded data stored on disk that represent interface values as gob was a developed in an era where there was one-to-one relationship between types and names.

Consider the following examples.

At some point in the past, we gob-encoded is stored on disk using something like:

type Rect struct { ... }
func (r Rect) Area() int { ... }
type Any struct {
	Shape interface{ Area() int }
}

func main() {
	gob.Register(Rect{})

	var b bytes.Buffer
	e := gob.NewEncoder(&b)
	e.Encode(Shape{Rect{2, 5}})
	
	// assume that b is stored on disk
	fmt.Printf("%q\n", b.String())
}

On the decoder side, this works fine with code like:

type Rect struct { ... }
func (r Rect) Area() int { ... }
type Any struct {
	Shape interface{ Area() int }
}

func main() {
	// Assume that the contents of r is loaded from disk as created by:
	// https://play.golang.org/p/dt0pW47u17P
	r := strings.NewReader(...)

	gob.Register(Rect{})

	d := gob.NewDecoder(r)
	v := new(Shape)
	fmt.Println(d.Decode(v)) // got nil; works as expected
}

At some point in the future, suppose we move Rect, now decoding fails:

type Rect = Rect2 // this is now a type alias to a different name
type Rect2 struct { ... }
func (r Rect2) Area() int { ... }
type Any struct {
	Shape interface{ Area() int }
}

func main() {
	// Assume that the contents of r is loaded from disk as created by:
	// https://play.golang.org/p/dt0pW47u17P
	r := strings.NewReader(...)

	gob.Register(Rect{})

	d := gob.NewDecoder(r)
	v := new(Shape)
	fmt.Println(d.Decode(v)) // got `gob: name not registered for interface: "main.Rect"`, expected nil
}

The problem is that the full type name is encoded in the gob wire data. Trying to decode the data at a later date does not know about the movement of the type due to type aliases.

Given that problem, one would think that you could just use gob.RegisterName to manually register the old name:

gob.RegisterName(Rect{})
gob.RegisterName("main.Rect", Rect{})

However, that panics with:

panic: gob: registering duplicate names for main.Rect2: "main.Rect2" != "main.Rect"

I propose the following changes:

  1. That gob be better documented about the interaction of type aliases and serialization of interface values.
  2. That gob.RegisterName be relaxed to permit duplicate names. However, this can cause confusion in the implementation as to which name to use when marshaling. Alternatively, we could add a gob.RegisterAlias that makes it clear that the duplicate names are aliases.
@dmitshur

This comment has been minimized.

Copy link
Member

@dmitshur dmitshur commented Jan 3, 2020

/cc @robpike per owners.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
2 participants
You can’t perform that action at this time.