upvote
Go has had this behaviour for promoting fields (provided they don't clash) for some time, this is just extending the language feature to initialisers.

Your contrived example doesn't initialise the embedded struct that also contains a "Burrow" field. If it did that at all, even without naming the Burrow field... you would not be allowed to initialise the struct, because of the ambiguity.

https://go.dev/ref/spec#Composite_literals

> A key must not denote a promoted field inside an embedded struct if that struct is also specified by another key.

> Given the declarations

    type Object  struct { name, color string }
    type Point3D struct { Object; x, y, z float64 }
    type Line    struct { Object; p, q Point3D }
> .... field selectors may not denote overlapping fields:

    obj   := Object{"edge", "black"}
    line3 := Line{Object: obj, name: "diagonal"} // invalid: name denotes a field inside Object
reply
Which error?

https://go.dev/play/p/CFWVXkFBOEX

Note that I added a name field to Line.

    Data:  {edge black}  --  {{edge black} {{ } 0 0 0} {{ } 0 0 0} diagonal}
Oops now Object.name is empty, which is my point.
reply

    ./prog.go:20:29: cannot specify promoted field name and enclosing embedded field Object
Which is what you get if you don't add a direct "name" field to Line, because it's then completely unambiguous, the deeper "name"s are not promotable.
reply
Yeah, but that isn't what I am talking about.

The whole point is the implicit bug, when the field is added and initialisation code rewritten to take advantage of this feature, without the developer realising the clash in first place.

reply
I'm not sure what I can say. One man's "source of bugs" is another man's "convenient syntax".

The rule errs in favour of the developer and the struct they can see. Initialising (or accessing!) a named field always picks the one in the top-level struct if you have one there. It'll be there because you added it. Promoted fields can only get promoted if they are unambiguous.

If you don't want to take advantage of that, you can write in full:

    g := Gopher{
        Name:    "Gopher",
        Burrow:  "Burrow #42",
        Habitat: Habitat{Burrow: "Wild Acres"},
    }
    fmt.Println("Your burrow: ", g.Burrow)
    fmt.Println("I mean your _real_ burrow: ", g.Habitat.Burrow)
... but most Go programmers would look at the fact you named two fields the same and then nested them as an unforced error, a rookie mistake.

Most of them are very happy that they can embed some other type they don't know the full contents of, knowing they can access (and now initialise!) fields in it they care about, and thus don't give the fields in their own types the same name, while resting assured that if that other type later gains new fields they've never heard of, it's not going to clash with their own naming choices and break their code and force them to rename something. Their types' field names always come out on top, in their code.

You're doing "but what if I deliberately named my type's fields the same as the embedded type's fields?", which is like "but what if I deliberately stuck my hand in the meat grinder?" -- don't do that

reply
The only, single, complaint is that this possible source of bugs should be part of go vet, just like in other programming languages static analysis tooling, Sonar, PVS, clang-tidy, Roslyn, Checkstyle, clippy, PMD,.... catch such kind of flaws.

However I see that I crash again in the Go versus other programming languages ecosystems mindset.

reply
I'd say it is as expected https://go.dev/play/p/sy6SMrOiw4y
reply
I would at least expect a go vet warning in such cases.
reply
Mhm, probably worth a golang-ci check for duplicate field names which are accessed by methods on the embedded type.
reply
Better would be a got vet check.

I understand the need not to break existing code that might have such fields.

reply
I think that's a no-go because vet checks have no-false-positive policy. Ie, when go vet flags something, its a bug.
reply
I would say that initialising the wrong field because a dev is unaware of a clash between Gopher.Burrow and Gopher.Habitat.Burrow (naturally in more complex code), when upgrading to 1.27 and taking advantage of the feature without realising it, is a bug.
reply