Neither. Return a blocking iterator, something like
type Iterator struct {
// Next blocks until a new message is available or the stream ends and returns if a new message is available.
Next() bool
// Message returns the current message. Must only be called after Next returns true.
Message() []byte
// Error returns the error reading the next message, if any.
Error() err
}
and let the user use their own channels/goroutines, if they need it.
That is what I would consider idiomatic Go. I would not consider it idiomatic to return/accept channels as part of your API anymore.
I definitely agree with Axel on the blocking / non-blocking thing. Digressing from the main point, but it may be more idiomatic to just return a concrete type, like *mqtt.Reader or *mqtt.Connection (if your package name is "mqtt"). The concrete type will have the iterator methods, plus any others for MQTT-specific stuff. For example:
reader, err := mqtt.Dial("url") // reader is type *mqtt.Reader
if err != nil { ... }
for reader.Next() {
msg, err := reader.Message()
if err != nil { ... }
}
if reader.Err() != nil { ... }
The reason is that then it's easy for callers to call MQTT-specific methods on the concrete type as well. In parts of the program that *take* a message reader, however, you'll probably use a locally-defined interface with just the methods you need. If the mqtt package defines the interface, it'll invariably include more methods than most people need.
Other considerations:
* Perhaps call the error method just Err(). That's common, for example, bufio.Scanner.Err.
* Maybe make mqtt.Reader.Message() return an mqtt.Message struct, which has Bytes() and Text() methods, as well as other MQTT message-specific stuff (topic, message ID, etc).
-Ben