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.
Be careful with Go struct embedding
21–30 of 101 posts
Re: Be careful with Go struct embedding
#22Earlier quoted context omitted.
See how it's used in the standard library io types, it makes for quite nice composition: https://go.googlesource.com/go/+/refs/heads/master/src/io/io...
I’m sympathetic to parts of the Go design philosophy, but the only thing that comes to mind looking at this is “damn, that’s some awkward (nominal-looking) syntax for (structural) intersection types”. (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.)
Re: Be careful with Go struct embedding
#23That’s actually crazy. Why is this even a feature?
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.
Re: Be careful with Go struct embedding
#24That’s actually crazy. Why is this even a feature?
Because type Foo struct { sync.Mutex whatever string } var foo Foo foo.Lock() foo.whatever = 42 foo.Unlock() is convenient.
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).
Re: Be careful with Go struct embedding
#25That’s actually crazy. Why is this even a feature?
See how it's used in the standard library io types, it makes for quite nice composition: https://go.googlesource.com/go/+/refs/heads/master/src/io/io...
Re: Be careful with Go struct embedding
#26Re: Be careful with Go struct embedding
#27That’s actually crazy. Why is this even a feature?
https://gcc.gnu.org/onlinedocs/gcc-5.3.0/gcc/Unnamed-Fields....
Re: Be careful with Go struct embedding
#28I'm surprised this wasn't in the recent post submitted here: https://blog.habets.se/2025/07/Go-is-still-not-good.html It's a one of a few rough edges in Go.
Re: Be careful with Go struct embedding
#29That’s actually crazy. Why is this even a feature?
Normally you wouldn’t contrive to use embedded struct fields in this way. And you can’t have the same kind of composition with methods - it’s a compiler error: https://go.dev/play/p/r04tPta1xZo So the whole article is basically about using the language in a way you normally would ever do.
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:
Re: Be careful with Go struct embedding
#30Earlier quoted context omitted.
Because type Foo struct { sync.Mutex whatever string } var foo Foo foo.Lock() foo.whatever = 42 foo.Unlock() is convenient.
almost always, the recommendation is to not embed your mutex; give it a name. 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.
But it's good advice when it works.