Sergi Mansilla
Sergi Mansilla

Reputation: 12793

`bufio.Writer` or `io.Writer`?

I have a function that writes data to anything that implements an interface with a Write(b []byte) (n int, err error) method. Right now in my program I write to an actual Conn, but following best practices (https://dave.cheney.net/2016/08/20/solid-go-design) and because I only call Write, I want to accept the minimum interface that implements that method. For that, I accept a parameter with interface io.Writer.

Since my function could be outputting lots of data very fast, should I be accepting a bufio.Writer instead? Or is it in the function’s consumer responsibility to use a buffered writer instead of a plain one? What are best practices?

Upvotes: 2

Views: 2753

Answers (1)

icza
icza

Reputation: 417592

Create your function to accept io.Writer, and document that it will write a lot of data, and so bufio.Writer or a similar construct is advised.

Do not limit users of your function to bufio.Writer, as you only use the functionality of an io.Writer. Also users may have other "buffered" implementation of io.Writer that will be sufficient for them.

Don't decide what's good for the users of your library, let them decide what's good for them. Should the users find bufio.Writer useful or better than their io.Writer implementation, they can always wrap it in a bufio.Writer and pass that (simply using bufio.NewWriter(w)).

If you create your function to accept io.Writer, users can always add the wrapping functionality very easily with a one-line utility function:

func wrapAndPass(w io.Writer) {
    yourFunction(bufio.NewWriter(w))
}

If you create your function to accept bufio.Writer, then there is no way for the users to undo this "wrapping". Users will be forced to always create and pass a bufio.Writer, whether it's needed or not.

Also you may opt to provide two functions: your original function taking io.Writer, and the above wrapping-and-passing utility function. If you do so, it would also be nice to check if the passed writer is already a *bufio.Writer, in which case wrapping should be avoided. Something like this:

func wrapIfNeededAndPass(w io.Writer) {
    if _, ok := w.(*bufio.Writer); !ok {
        w = bufio.NewWriter(w)
    }
    yourFunction(w)
}

But usually this kind of wrapping is only applied if extra functionality is needed "beyond" io.Writer.

Upvotes: 9

Related Questions