Your proposed signature has a little problem and that is that when calling result, err := reader.Read(nil)
the Read method doesn’t know how many bytes it should read. The incoming buffer parameter actually serves two purposes. First is to provide a place for storage. The second is to tell how many bytes should be read. If you want to read all the bytes from a reader, you can use https://golang.org/pkg/io/ioutil/#ReadAll If you want to read N bytes into a freshly allocated buffer, you can make a helper function: func ReadNBytes(r io.Reader, n int) (result []byte, err error) { result = make([]byte, n) n, err := r.Read(result) return result[:n], err } On Fri, 6 Sep 2019 at 00:48 Tom Payne <twpa...@gmail.com> wrote: > Dave Cheney has written (yet another) excellent blog post on Go, on the > subject of API design and caller-controlled allocations: > > https://dave.cheney.net/2019/09/05/dont-force-allocations-on-the-callers-of-your-api > > In this he compares two possible signatures for the io.Reader.Read method: > func (r *Reader) Read(buf []byte) (int, error) > func (r *Reader) Read() ([]byte, error) > and makes a very good case for why the first, albeit trickier to use, is > the better API. The post also sets up a false dichotomy/straw man argument, > comparing only two signatures when there are other alternatives too. > > What about the following API: > func (r *Reader) Read(buf []byte) (result []byte, err error) > If the buf argument is non-nil then buf is used to store the result, > len(result) == the int returned by the actual Read(buf []byte) (int, error) > method. > If the buf argument is nil then a new []byte is created and returned, i.e. > the allocation is done by Read and the method is as easy to use as Read() > ([]byte, error). > The semantics of the error return remain the same (i.e. Read might return > both some bytes read and an error), but that is orthogonal to the memory > allocation. > An example of this pattern is here > <https://github.com/twpayne/go-polyline/blob/master/polyline.go#L68-L76>. > > Given that slices are small and cheap to copy (a pointer to the data, a > length, and a capacity) what are the downsides to using > func (r *Reader) Read(buf []byte) (result []byte, err error) > as a method? > > I am not suggesting that we change io.Reader (that would break > everything!), just looking for input into good Go API design. > > This would be a comment on Dave's fantastic blog, but the blog does not > have comment functionality, so I'm asking here, and I know that Dave lurks > here too. > > Cheers, > Tom > > -- > You received this message because you are subscribed to the Google Groups > "golang-nuts" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to golang-nuts+unsubscr...@googlegroups.com. > To view this discussion on the web visit > https://groups.google.com/d/msgid/golang-nuts/8ffb9828-c6e2-47de-bca1-ef6988f081e8%40googlegroups.com > <https://groups.google.com/d/msgid/golang-nuts/8ffb9828-c6e2-47de-bca1-ef6988f081e8%40googlegroups.com?utm_medium=email&utm_source=footer> > . > -- You received this message because you are subscribed to the Google Groups "golang-nuts" group. To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/CAO6k0uvEZgHm9ppQ8sA1K-M8fA2WENf0tJG1KyTov7PXAt6GcQ%40mail.gmail.com.