list.delete(value)
without realizing it involves a linear search is considered one of the benefits of Go.That said, I agree Go could use a bit for ergonomic and handy methods, while still remaining efficient.
The example on Concurrency also has a different answer. Go gives you all the tools, but there are many different ways their problem can be solved. By explicitly avoiding a "join" method and having any default 'channels', Go makes all these possible. What is missing is some of the popular patterns that emerged in the last few years need to make it to into stdlib or some cookbooks.
1. If you want a worker model, I would start n=3 go-routines and they all receive from a single channel. They don't need to much around with a channel of buffer 3, as in the example.
2. If the workers already return data from a compute, the read from driver serves as the wait.
3. In other cases, there is sync.Waitgroup available to synchronize completion status.
4. End of work from the driver can be indicated via a channel close. Closed channels can still be read from until they are empty and the reader can even detect end-of-channel.
Designing concurrent system is a bit complicated. Some tutorials do make it sound like a `go` keyword is all you need. All of these can fixed by improving std-lib or cookbooks.