Go: proposal: io: add interface type StringBytesWriter

Created on 19 May 2018  Â·  8Comments  Â·  Source: golang/go

I propose that we add to package io:

type StringBytesWriter interface {
    io.Writer
    io.StringWriter  // the unexported stringWriter should be changed
    io.ByteWriter
}

This would solve a lot of inconveniences and improve efficiency in many places where a Writer represents something that writes string (or textual) data.

I often find that it’s undesirable for some exported functions to take as arguments or return a type like bytes.Buffer or *bufio.Writer or *strings.Builder when users of a package in these cases should only be writing data, not otherwise modifying the state of the concrete type. All of these three types (bytes.Buffer, *bufio.Writer, and *strings.Builder) and _many more outside the stdlib_ already implement the proposed StringBytesWriter interface: it can be much more efficient to write a single byte or directly copy a string to the Writer without having to allocate a slice.

FrozenDueToAge Proposal WaitingForInfo

Most helpful comment

I'm not sure I follow how exposing this interface would improve efficiency. Interfaces are satisfied implicitly, so it does not matter whether or not the io package defines them. You might say adding it is more convenient, but it should not affect performance.

I'm also not sure that exposing interfaces that combine existing exposed interfaces is that helpful. Perhaps a better proposed change would be to expose io.StringWriter, as a start.

All 8 comments

An alternative name: io.TextWriter

I'm not sure I follow how exposing this interface would improve efficiency. Interfaces are satisfied implicitly, so it does not matter whether or not the io package defines them. You might say adding it is more convenient, but it should not affect performance.

I'm also not sure that exposing interfaces that combine existing exposed interfaces is that helpful. Perhaps a better proposed change would be to expose io.StringWriter, as a start.

@mvdan you'd get better efficiency when you provide external packages the interface I described for writing instead of just an io.Writer when the writer is intended for string data. The point is that, if you've already got a string and you need to write it to an io.Writer, you need to convert it to a []byte, and this is an allocation. And if you've only got an io.Writer for writing, and you want to write a single byte, you need to make a slice out of it to pass it into Write. (But, as part of the Go language itself, strings and single bytes can be copied directly to any []byte.)

@dchenk
For completeness did you consider including a rune writer, which is also implemented by *bytes.Buffer, *bufio.Writer, and *strings.Builder?

many more outside the stdlib already implement

Can you please list a couple from popular repos? It might add weight to the proposal to see such examples in the wild.

@dchenk I understand the point of writing single bytes and strings. My point is that you can do this optimization without the io package exposing the interface. For example:

package p

type StringBytesWriter interface {
    io.Writer
    WriteString(string) (n int, err error)
    io.ByteWriter
}

func DoSomething(w StringBytesWriter, foo *Foo) { ... }

But note that not even that is necessary. You could do:

func DoSomething(w io.Writer, foo *Foo) {
    if fastWriter, ok := w.(StringBytesWriter); ok {
        // faster implementation
    }
    // slower implementation
}

This is what many parts of the standard library do. This way, io.Writers still work, but passing in types that implement the more advanced interfaces still get the better performance. For example, see io.Copy's godoc:

If src implements the WriterTo interface, the copy is implemented by calling src.WriteTo(dst). Otherwise, if dst implements the ReaderFrom interface, the copy is implemented by calling dst.ReadFrom(src).

@meirf
https://godoc.org/github.com/philhofer/fwd#Writer
https://godoc.org/github.com/free/concurrent-writer/concurrent
The point is not just that there are already a lot of implementation of this interface; these types in the stdlib (bytes.Buffer, strings.Builder, bufio.Writer) are used like this in a lot of places everywhere:

someWriter.Write([]byte("some string"))

and

someWriter.Write([]byte("\n"))  // also: someWriter.Write([]byte{'\n'})

If you use io.WriteString it uses the WriteString method if it's there and if not it does the conversion to []byte:

io.WriteString(someWriter, "some string")

and

io.WriteString(someWriter, "\n")

To be a viable proposal, this issue needs more detail.

Please include concrete examples (ideally drawn from experience reports) that illustrate the motivating problem, then show how those examples would be improved by it. (For example, if the motivating problem is a performance issue, quantify the impact on a real program: how much time is being spent today in type-assertions that this proposal would eliminate?)

Was this page helpful?
0 / 5 - 0 ratings