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/json: panic on embedded struct pointer that implements Unmarshaler #30148
Comments
I don't think there's anything that we can do without changing the The fundamental problem is that embedded types have their methods forwarded to the parent. Thus, type |
Also, the fact that you can't directly call var b B
err := b.UnmarshalJSON([]byte(`{"hello": "world"}`))
fmt.Println(err) // json: Unmarshal(nil *main.A) |
I guess fixing this would require extending Any sort of input on the zero value of |
There is no way in Go to embed a type and not have it's method set be forwarded. Embedding forwards both fields and methods and is a common gotcha in Go and a source of bugs. That said, you can destructively cancel out the forwarded method if another embedded type has the same method. In such a case, the parent type will not have forwarded methods that have naming conflicts. Consider this modified example of yours: https://play.golang.org/p/UnR4h63VkwH However, I should note that this approach is a hack. In general I recommend against the use of embedding. It tends to cause more problems than it solves. |
@segevfiner is this still a bug that you want addressed, or do you feel that your question has been answered? If you think this is more of a question, and less of a bug at this point, you can continue the conversation here: https://golang.org/wiki/Questions |
Definitely a bug. If an embedded value works, why shouldn't an embedded pointer? Also considering that this is not documented anywhere, and calls the user |
@segevfiner thanks for following up. /cc @rsc |
I disagree.
Embedded pointers already have asymmetric behavior compared to the non-pointer versions. Examples: // type B { *A; *a }
b.A = 5 // may panic for embedded pointer, never panics for embedded value
b.a = new(int) // impossible to initialize an embedded unexported pointer (see #21357) I don't believe asymmetry here is an argument that this is a bug.
The
The I don't think there is anything actionable here for |
Whether you call this a bug or not. It's still a behavior that surprised me and I didn't expect. Of course they have different/asymmetric behavior, but Go does try to hide some of the differences where it can between pointers and values. Not all cases of course since they arw different after all. Do consider that this can easily happen in some deeply nested struct inside some other struct and maybe even on some slice of such structs which allocates items dynamically. Yeah Except documenting this, if there is any convention of documenting such gotchas... It's quite possible that fixing this would require extending/modifying |
Embedded types are in general difficult to use properly and naturally lead to surprisingly spooky action at a distance. This is a general gotcha that isn't specific to |
I'm going to close this issue, then, and we can re-open if this needs to be evaluated again later on. |
If it's impossible to allocate a *nestedField couldn't we alternatively check within the indirect function if the Unmarshaller belongs to a struct, and if so loop over the anonymous fields and if only one anonymous field contains the Unmarshaller interface return an error that it's unsupported instead of panicking? |
What version of Go are you using (
go version
)?Does this issue reproduce with the latest release?
Yes.
What operating system and processor architecture are you using (
go env
)?go env
OutputWhat did you do?
https://play.golang.org/p/c6Q_ehlEHMQ
What did you expect to see?
Unmarshaling works.
What did you see instead?
Analysis
This is caused by
encoding/json
not initializing (new
) the embedded struct pointer field. Type asserting the embedding struct toUnmarshaler
works, with the interface value holding a pointer to the embedding struct, but on calling theUnmarshalJSON
method it gets called with the embedded struct as the receiver, which isnil
.Also see go-yaml/yaml#436
The text was updated successfully, but these errors were encountered: