The io.Writer
interface, as shown in the following code, is just as simple as its reader counterpart:
type Writer interface { Write(p []byte) (n int, err error) }
The interface requires the implementation of a single method, Write(p []byte)(c int, e error)
, that copies data from the provided stream p
and writes that data to a sink resource such as an in-memory structure, standard output, a file, a network connection, or any number of io.Writer
implementations that come with the Go standard library. The Write
method returns the number of bytes copied from p
followed by an error
value if any was encountered.
The following code snippet shows the implementation of the channelWriter
type, a writer that decomposes and serializes its stream that is sent over a Go channel as consecutive bytes:
type channelWriter struct { Channel chan byte } func NewChannelWriter() *channelWriter { return &channelWriter{ Channel: make(chan byte, 1024), } } func (c *channelWriter) Write(p []byte) (int, error) { if len(p) == 0 { return 0, nil } go func() { defer close(c.Channel) // when done for _, b := range p { c.Channel <- b } }() return len(p), nil }
golang.fyi/ch10/writer1.go
The Write
method uses a goroutine to copy each byte, from p
, and sends it across the c.Channel
. Upon completion, the goroutine closes the channel so that consumers are notified when to stop consuming from the channel. As an implementation convention, writers should not modify slice p
or hang on to it. When an error occurs, the writer should return the current number of bytes processed and an error.
Using the channelWriter
type is simple. You can invoke the Write()
method directly or, as is more common, use channelWriter
with other IO primitives in the API. For instance, the following snippet uses the fmt.Fprint
function to serialize the "Stream me!"
string as a sequence of bytes over a channel using channelWriter
:
func main() { cw := NewChannelWriter() go func() { fmt.Fprint(cw, "Stream me!") }() for c := range cw.Channel { fmt.Printf("%c ", c) } }
golang.fyi/ch10/writer1.go
In the previous snippet, the serialized bytes, queued in the channel, are consumed using a for…range
statement as they are successively printed. The following snippet shows another example where the content of a file is serialized over a channel using the same channelWriter
. In this implementation, an io.File
value and io.Copy
function are used to source the data instead of the fmt.Fprint
function:
func main() { cw := NewChannelWriter() file, err := os.Open("./writer2.go") if err != nil { fmt.Println("Error reading file:", err) os.Exit(1) } _, err = io.Copy(cw, file) if err != nil { fmt.Println("Error copying:", err) os.Exit(1) } // consume channel for c := range cw.Channel { fmt.Printf("%c ", c) } }
golang.fyi/ch10/writer2.go.
18.117.93.0