Thanks for the clarification, and I agree completely. There are plenty of cases in the Go standard library where there is a public struct for bundling state, and I don't see adding a new member (whether it's private or public) as a breaking change. Nobody's going to attempt to mirror the entire type tree, and generally they can't because there are private members too.
Equally, it's reasonable to allow a package to export a plain-old-data struct, certainly between packages that comprise a user application. On Friday, 12 March 2021 at 12:15:15 UTC axel.wa...@googlemail.com wrote: > On Fri, Mar 12, 2021 at 10:16 AM Brian Candler <b.ca...@pobox.com> wrote: > >> I can see two separate things under discussion - compatibility of struct >> types for conversion, and backwards-compatibility of serialization [JSON? >> Protobuf?] >> > > Backwards-compatibility of serialization is a red herring. It is off-topic > as far as this thread is concerned, this is purely about language-level > compatibility of types. > > >> but I can't see what's being proposed to change. >> > > I think the proposed change can be summarized as roughly > > Either >> 1. Create consensus that adding an (exported) field to a struct should be >> considered a backwards incompatible change, or >> 2. Create consensus that converting one struct type into a different >> struct type should be considered deprecated, as it makes you vulnerable to >> getting broken by someone adding a field. >> Take either consensus and put it into the Go 1 compatibility promise and >> potentially encode it into a vet check. > > > That is my understanding of what's "proposed". Personally, as I said, I'm > not in favor of either. I think both statements lack nuance. > > The logical conclusions of the arguments brought forth is that every > publicly visible change is a breaking change. So if we follow that logic > we'd end up (in my opinion) in an ecosystem where every module is > constantly incrementing their major versions because "we technically broke > the API". I think that is undesirable. > > Given that every publicly visible change to an exported identifier can be > considered a breaking change, I personally don't think it's all that useful > to talk about interface-changes vs. struct changes and which of them are > breaking. Both are - in some circumstances. And in some circumstance, > neither are. It's more useful to talk about *how frequently* any given > change leads to breakages. I think it's easy to make an argument that > changing in interface type will lead to very frequent breakages - it will > either break every implementation of the interface, or any usage of the > interface, or both. > > Meanwhile, I think it's also not that hard to make an argument that > breakages causing by converting one struct type into another and then > adding a field to one of them are *comparatively* rare. And even within > that, there is nuance, where it is more frequent for some kind of types > (POD - plain-old-data) than others (e.g. http.Server or other types used to > abstract and bundle state). > > That's why I don't think a vet check would work. It can't distinguish the > case of a struct being POD vs. the struct bundling state. And I would even > argue that generally, the "bundling state" case is more common in Go. > > >> I don't follow the argument that "adding a field to a struct is *now* a >> backwards incompatible change". Surely this was always the case, >> regardless of tags? That is, if you have >> >> type T1 struct { >> Foo string >> } >> >> type T2 struct { >> Foo string >> Bar string >> } >> >> then what would you expect to happen for: >> >> v1 := T1{} >> v2 := T2(v1) ?? >> ... >> v2 := T2{} >> v1 := T1(v2) ?? >> >> These two structures have different shapes. In the first case, I suppose >> it could copy the matching members and create zero values for the new >> ones. In the second case, I suppose it could copy the matching members and >> silently(!) discard the missing ones. But either way, that just means the >> compiler magically writing code which copies member-by-member. I'd rather >> do this by hand. >> >> Even with magic conversions, trying to use a *t1 as a *t2 or vice versa >> would definitely not work, as they have differing layout in memory. Indeed, >> even the ordering of fields in memory must be the same for two structs to >> be compatible: >> https://play.golang.org/p/BTUc6mNJQKS >> >> However, if two structures have the same members in the same order, and >> differ only by tags, then they have the same shape. Then it seems >> reasonable to me to treat a T1 as compatible with T2; copying can be done >> as a blob of bytes. The change in 1.8 to omit comparing tags has made >> types more compatible, not less. >> >> Separately from that, there's the issue of serialization and >> forwards/backwards compatibility. I don't see any problem here. For >> example, if you serialize a type T1 to JSON, and then deserialize to T2 >> (which may have more or fewer fields), that all works fine. >> >> https://play.golang.org/p/aJwObgyRhan >> >> If you want to reject unexpected fields when deserializing, there are >> options for doing that. But by default, they are compatible in both >> directions. You can also re-order the fields in the struct, since the JSON >> deserialization is assigning elements individually. >> >> -- >> > You received this message because you are subscribed to the Google Groups >> "golang-nuts" group. >> To unsubscribe from this group and stop receiving emails from it, send an >> email to golang-nuts...@googlegroups.com. >> > To view this discussion on the web visit >> https://groups.google.com/d/msgid/golang-nuts/36e1a048-2505-4841-8882-a9b16a33fd57n%40googlegroups.com >> >> <https://groups.google.com/d/msgid/golang-nuts/36e1a048-2505-4841-8882-a9b16a33fd57n%40googlegroups.com?utm_medium=email&utm_source=footer> >> . >> > -- You received this message because you are subscribed to the Google Groups "golang-nuts" group. To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/9f50c4ad-13ae-45ad-a874-08c6dd63e7c9n%40googlegroups.com.