Using channels to talk between goroutines is the perfect choice. There’s plenty of go API’s that require a chan of a type.
The whole purposes of having such built-in channel as a part of the core language is to encourage everyone to use it whenever possible, the reason is dead simple - it is much less likely to shoot yourself in the foot.
Done() <- chan struct{}
What are you trying to say? That using a chan as a function argument can shoot yourself in the foot? The whole idea behind channels is to not shoot yourself in the foot with mutex's, deadlocks, etc. What are you trying to say? Wrapping a chan in a type handle is safer than passing a chan of a type? Nope.I appreciate your concerns but one could say similar things about nil pointers or accessing slices out of range. Anything you use should be handled with care.
you can't abstract over channels easily unless you're missing the point of channels.