Still worth being careful, but it can be useful when you have a set of common fields that everything of a certain group will have (such as a response object with basic status, debug info, etc. and then additional data based on the particular struct). I don't know why they let you embed multiple layers and multiple objects though. I've never gotten value out of anything but a "here's a single set of common fields struct embedding".
The fascinating bit to me is that there is a consolidateMultiples function in go/src/go/types/lookup.go (lines 286-304) that detects when multiple embedded types at the same depth provide the same field name. I wonder why they don’t do this for all levels. How deep could this even be in practice for it to matter? You could just have a hashmap with them all.
While it may seem questionable for fields; it applies to methods too and is potentially more useful as a way to override them when doing struct embedding but wanting to preserve an interface.
I hope the feature mentioned in the article will cause a compiler error.
However, I wouldn't use this approach when writing my own code.
Read the article. It won't.
At best you can perhaps find a linter that'll report it?
> However, I wouldn't use this approach when writing my own code.
You might use it by accident.
It's a one of a few rough edges in Go.
I do not embed structs anymore. It is almost always a mistake. I would confidently place it in the "you should be required to import 'unsafe' to use this feature" bin.
I was very surprised that either example compiled, though.
tymscar•2h ago
jrockway•2h ago
echelon•2h ago
Any coding construct that can cause defects is an antipattern. Your language should discourage defects by design. Especially if the faults crop up at runtime.
This struct field dereferencing is like NULLs and "goto".
Language design that is anti-defect yet ergonomic include the modern Option<T> and Result<T, E> as seen in languages such as Swift and Rust, with first class destructuring that doesn't make it painful to use. They're almost impossible to misuse, yet feel convenient instead of frictionful. Rust's sum types and matching are another set of examples. Hopefully these patterns spread to more languages, because they're safe and convenient.
eru•10m ago
> Language design that is anti-defect yet ergonomic include the modern Option<T> and Result<T, E> as seen in languages such as Swift and Rust, with first class destructuring that doesn't make it painful to use.
Funny enough, this is only 'modern' in imperative languages. It's been a staple in the ML family since approximately forever. (But hey, I do appreciate progress when we get it!)
thrill•1h ago
sethammons•1h ago
foo.mu.Lock()
This way you don't expose your primitives, preventing poor usage from causing a deadlock. Generally you don't want the user of your struct to have to know when or when to not lock.
eru•11m ago
But it's good advice when it works.
resonious•35m ago
It is a bit ironic that this language that was was designed around "all of these features of other languages cause trouble, we will omit them" also has a bunch of features that cause trouble and get avoided.
Just to make my own stance clear: I like language features. I think this struct embedding feature looks pretty cool. But I also like interfaces and polymorphism. I think it's OK for a programming language to be powerful, and to put the onus on developers to not go too crazy with that power. And for that reason, I've always gravitated away from Go, and always jump on an opportunity to make fun of it (as I have here).
mikepurvis•2h ago
gdbsjjdn•2h ago
metadat•2h ago
ShroudedNight•1h ago
dgl•1h ago
mananaysiempre•1h ago
(It also feels to me that this sort of anonymous embedding is materially different for interfaces vs structs, though I admit that from a type-theoretic perspective it’s not.)
bilbo-b-baggins•46m ago
whatevertrevor•22m ago
mananaysiempre•1h ago
[1] http://doc.cat-v.org/plan_9/4th_edition/papers/comp, look for “anonymous structure or union” and note that a (different) part of that extension has since been standardized.
bilbo-b-baggins•53m ago
https://go.dev/play/p/r04tPta1xZo
So the whole article is basically about using the language in a way you normally would ever do.
whatevertrevor•12m ago
https://go.dev/play/p/D3eFi9_can8
Conflicting functions at nested levels also compile:
https://go.dev/play/p/xXXDZCjQJOh
It's not about method vs field, it's about the nesting level of the conflicting identifier, if it's at the same level there's an error, if it's at different levels, the higher level hides the lower level identifier:
https://go.dev/doc/effective_go#embedding
amiga386•42m ago
https://go.dev/doc/effective_go#embedding
> Embedding types introduces the problem of name conflicts but the rules to resolve them are simple. First, a field or method X hides any other item X in a more deeply nested part of the type. If log.Logger contained a field or method called Command, the Command field of Job would dominate it.
> Second, if the same name appears at the same nesting level, it is usually an error; it would be erroneous to embed log.Logger if the Job struct contained another field or method called Logger. However, if the duplicate name is never mentioned in the program outside the type definition, it is OK. This qualification provides some protection against changes made to types embedded from outside; there is no problem if a field is added that conflicts with another field in another subtype if neither field is ever used.
typ•17m ago
https://gcc.gnu.org/onlinedocs/gcc-5.3.0/gcc/Unnamed-Fields....