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: deserializes internal Struct{&0} as nil #12039

Closed
divVerent opened this issue Aug 5, 2015 · 7 comments
Closed

encoding/gob: deserializes internal Struct{&0} as nil #12039

divVerent opened this issue Aug 5, 2015 · 7 comments

Comments

@divVerent
Copy link

Until now I thought gob serializing + deserializing will recreate the object so that it is equal to the original according to reflect.DeepEqual. In fact, isn't this the one most basic requirement for serialization?

However, this is a case where it won't:

http://play.golang.org/p/CWP5m7RKIg

Expected behavior: for any object a, gob.NewEncoder(...).Encode(a) will

  • either fail
  • or write a buffer that, when gob.NewDecoder(...).Decode()'d, will yield a value b with reflect.DeepEqual(a, b).

Actual behavior: it silently turns the allocated zero into a nil.

Note that gob's documentation is ambiguous about this; it first writes:
"Pointers are not transmitted, but the things they point to are transmitted; that is, the values are flattened."
which is nice, but later - in the encoding details - writes:
"If a field [of a struct] has the zero value for its type, it is omitted from the transmission."

When reading it first, I assumed this "zero value" only includes nil because the field is pointer-typed. However, it seems like the zero value of the underlying type is considered a zero value here too.

Therefore I suppose there are two ways to fix it:

  1. Make gob support above requirement in conjunction with reflect.DeepEqual.
  2. Introduce another module that does.
@robpike
Copy link
Contributor

robpike commented Aug 6, 2015

A consequence of the design: If every field is zero, there is simply no way to transmit the existence of the item on the other end. And who is to say that is not the correct behavior anyway? In many cases it will be.

Working as intended when it's not just unfortunate.

@robpike robpike closed this as completed Aug 6, 2015
@divVerent
Copy link
Author

Why is there no way to transmit existence? For a raw *int, gob can
distinguish &0 and nil. Only inside a struct this breaks.

On Wed, Aug 5, 2015, 21:16 Rob Pike notifications@github.com wrote:

Closed #12039 #12039.


Reply to this email directly or view it on GitHub
#12039 (comment).

@robpike
Copy link
Contributor

robpike commented Aug 6, 2015

It is inherent in the design. Top-level items are always sent even if they are zero, whatever their type (http://play.golang.org/p/VJxmBFGnFH). Internal items are not, and the difference between T and *T in an internal item has no bearing on what is transmitted. This is the design.

Working as intended.

@divVerent
Copy link
Author

It is very unfortunate then that Go has no lossless data serializer (e.g.
encoding/json isn't better because it loses some location info from
time.Time and doesn't support nan/inf floating point values).

Can you at least change gob's documentation to make this more clear?
Currently it can be read to only not encode nil pointers, because &0 is not
the zero value for *int (only nil is).

Also, information losses of the encoding shouldn't be hidden in the wire
protocol description but be in a more prominent place - ideally near the
part that tells about flattening pointers (which BTW makes me immediately
realize that for **int, nil and &nil aren't distinguished).

Would that be viable?

On Wed, Aug 5, 2015, 22:58 Rob Pike notifications@github.com wrote:

It is inherent in the design. Top-level items are always sent even if they
are zero, whatever their type (http://play.golang.org/p/VJxmBFGnFH).
Internal items are not, and the difference between T and *T in an internal
item has no bearing on what is transmitted. This is the design.

Working as intended.


Reply to this email directly or view it on GitHub
#12039 (comment).

@robpike
Copy link
Contributor

robpike commented Aug 6, 2015

The question of what is guaranteed is a little fuzzy.

There might be a way to do this, but I worry about compatibility. It may be possible to send a zero struct but not its contents but is sure to break some user. Re-opening for trial early in 1.6.

@robpike robpike reopened this Aug 6, 2015
@robpike robpike changed the title encoding/gob: deserializes Struct{&0} as nil encoding/gob: deserializes internal Struct{&0} as nil Aug 6, 2015
@robpike robpike self-assigned this Aug 6, 2015
@robpike robpike added this to the Go1.6Early milestone Aug 6, 2015
@robpike
Copy link
Contributor

robpike commented Sep 2, 2015

Working as intended, if not the way you want.. A zero struct should behave the same as a zero int. There is no notion of "existence" only "non-zero".

@robpike robpike closed this as completed Sep 2, 2015
@divVerent
Copy link
Author

http://play.golang.org/p/h-F_TgKdcj

A zero struct certainly behaves different from a zero int.

If this is WAI, then I'd highly appreciate if the documentation of gob could be changed to actually explain these things.

@golang golang locked and limited conversation to collaborators Sep 4, 2016
@rsc rsc unassigned robpike Jun 23, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

3 participants