Because
type Foo struct {
sync.Mutex
whatever string
}
var foo Foo
foo.Lock()
foo.whatever = 42
foo.Unlock()
is convenient.Because
type Foo struct {
sync.Mutex
whatever string
}
var foo Foo
foo.Lock()
foo.whatever = 42
foo.Unlock()
is convenient.
I thought Go was all about being "simple" at the cost of convenience.
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).
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.
Alas, locks don't compose, ie often your users will have to know about the internals when you are using locks.
But it's good advice when it works.
Hmm, never realized the convenience came this way. Seems the compiler could emit a warning if two equal depth names might cause confusion, which could be ignored if acceptable.
It's dangerous. This is awful.
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.
I mostly agree.
> 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!)