I think it's clear that *any* change to an interface - including adding or 
removing methods, or changing the arguments or return types of any method - 
must be a breaking change.

- *Adding* a method to an interface means that a concrete type which 
implemented the interface before, won't satisfy it now
- *Removing* a method from an interface means that code which accepted a 
value of that interface type and called that method, won't be able to now

Structures are a bit different:

- Adding fields to a structure, or changing their order, will not break 
code that accesses it member-by-member (as long as the zero-value is a 
sensible default)
- But it *will* break code that depends on the exact shape of the 
structure, e.g. if you create another type with the same shape and assign 
the whole thing as a single value

ISTM that consumers shouldn't do that.  In the general case (where the 
struct is in a different package and contains unexported fields) they can't 
anyway.

An interface which contains unexported methods is different beast.  AFAICS, 
you can't satisfy such an interface unless you embed another type which 
satisfies it.

On Friday, 12 March 2021 at 09:50:59 UTC Colin Arnott wrote:

> > Hence breakage seems entirely reasonable to me, to force the user of T1 
> to decide how to handle it.
>
> This feels like saying, if a customer implements an interface and that 
> interface adds a method, it is up to the user to decide how to handle it.
>
> There is even a suggestion to use private method like how you suggested 
> using private fields. The only difference is adding methods to all public 
> interfaces IS defined as a breaking and generally anticanon.
>
> On Fri, 12 Mar 2021, 09:35 Brian Candler, <b.ca...@pobox.com> wrote:
>
>> Maybe the argument goes something like this:
>>
>> * package P exports a struct type T1
>> * package Q deals with values of type T1
>> * package Q wants to serialize objects of type T1, but T1 doesn't have 
>> the desired set of tags; so it defines a new type T2 which is the same as 
>> T1 but with different tags
>> * it uses v2 := T2(v1) and then proceeds to serialize it
>> * if package P changes the type T1, then this breaks package Q
>>
>> If that's the problem statement, then I'd say:
>>
>> 1. if T1 has any private fields, it's impossible to make a T2 which is 
>> compatible with it anyway (AFAICS).
>> https://play.golang.org/p/5QND-BdsMuR
>>
>> 2. if T1 changes to add a new public field, then the consumer *must* make 
>> a decision as to what to do about this extra field.  Should it exclude it 
>> from the JSON representation, or include it, or include it as 'optional' 
>> (i.e. present if non-zero)?  What JSON key should it use?  Often you don't 
>> want the default capitalization of the name.
>>
>> Hence breakage seems entirely reasonable to me, to force the user of T1 
>> to decide how to handle it.
>>
>> On Friday, 12 March 2021 at 09:15:45 UTC Brian Candler wrote:
>>
>>> On Friday, 12 March 2021 at 06:56:46 UTC axel.wa...@googlemail.com 
>>> wrote:
>>>
>>>> On Fri, Mar 12, 2021 at 3:26 AM Robert Engels <ren...@ix.netcom.com> 
>>>> wrote:
>>>>
>>>>> I must be dense on this. I have no idea why there is any use of struct 
>>>>> tags or JSON marshaling in the sample code.
>>>>>
>>>>
>>>> To demonstrate why converting between different struct types is useful.
>>>>
>>>> If you want take an struct existing type (defined in a different 
>>>> package) and change how it is marshalled (say, omit or rename a certain 
>>>> field) you'd have to create a new struct type with different tags and copy 
>>>> over the fields. This was deemed inconvenient 
>>>> <https://github.com/golang/go/issues/6858>, so it was changed to the 
>>>> current semantics in go 1.8 
>>>> <https://urldefense.proofpoint.com/v2/url?u=https-3A__golang.org_doc_go1.8-23language&d=DwMFaQ&c=ncDTmphkJTvjIDPh0hpF_w&r=OaiRsMTFeJ0tMmMJQ5CwMuxNMvWoKXAJr-ebE3c5HU8&m=ymdbzPHlqzAZKoexWVIiBZtzqcg1g-TeYfTzKTHHkHU&s=RtGntkDdl9C6XLqF_LM45AUHEnrTTziLgrYa2fc8fdw&e=>.
>>>>  
>>>> OP is pointing out that the change means adding a field to a struct is now 
>>>> a backwards incompatible change, as someone might to this conversion.
>>>>
>>>
>>> I think I am also being dense.  I can see two separate things under 
>>> discussion - compatibility of struct types for conversion, and 
>>> backwards-compatibility of serialization [JSON? Protobuf?] - but I can't 
>>> see what's being proposed to change.
>>>
>>> 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 
>>> <https://urldefense.proofpoint.com/v2/url?u=https-3A__play.golang.org_p_BTUc6mNJQKS&d=DwMFaQ&c=ncDTmphkJTvjIDPh0hpF_w&r=OaiRsMTFeJ0tMmMJQ5CwMuxNMvWoKXAJr-ebE3c5HU8&m=ymdbzPHlqzAZKoexWVIiBZtzqcg1g-TeYfTzKTHHkHU&s=6ov1ae-sS1KtoTu_dMdvwIRmKHwAbHvUvkoKvCWxK3U&e=>
>>>
>>> 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 
>>> <https://urldefense.proofpoint.com/v2/url?u=https-3A__play.golang.org_p_aJwObgyRhan&d=DwMFaQ&c=ncDTmphkJTvjIDPh0hpF_w&r=OaiRsMTFeJ0tMmMJQ5CwMuxNMvWoKXAJr-ebE3c5HU8&m=ymdbzPHlqzAZKoexWVIiBZtzqcg1g-TeYfTzKTHHkHU&s=t3P3cSzA1eKzO-XyW7zdxgt5vvFTQUEezS1UY9a8atQ&e=>
>>>
>>> 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 a topic in the 
>> Google Groups "golang-nuts" group.
>> To unsubscribe from this topic, visit 
>> https://groups.google.com/d/topic/golang-nuts/oAjxpH-Qq2Y/unsubscribe.
>> To unsubscribe from this group and all its topics, send an email to 
>> golang-nuts...@googlegroups.com.
>> To view this discussion on the web visit 
>> https://groups.google.com/d/msgid/golang-nuts/d091ff96-0407-4e82-b098-ffd4b37f6377n%40googlegroups.com
>>  
>> <https://groups.google.com/d/msgid/golang-nuts/d091ff96-0407-4e82-b098-ffd4b37f6377n%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/89c7ce82-bbc9-4617-970e-ccce4476155en%40googlegroups.com.

Reply via email to