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
Assignees
Milestone

Comments

@divVerent
Copy link

@divVerent divVerent commented Aug 5, 2015

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 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 Aug 6, 2015
@divVerent
Copy link
Author

@divVerent divVerent commented Aug 6, 2015

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

@divVerent divVerent commented Aug 6, 2015

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 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 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 Sep 2, 2015
@divVerent
Copy link
Author

@divVerent divVerent commented Sep 2, 2015

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
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
3 participants
You can’t perform that action at this time.