What should io.Write returns?


I was learning go and doing some tests with the language when I found this weird behavior in my code. I create two types to demonstrate my findings and implemented the interface io.Write on both. This little program downloads the content of a web page and prints it to the console.
The BrokenConsoleWriter keeps track of the bytes written by fmt.Println and any error it may "throw" and returns it. On the other hand, the ConsoleWriter simply ignore the return of fmt.Println and returns the total length of the slice and nil for the error.

When I run the program, the BrokenConsoleWriter doesn’t print the entire html content while that ConsoleWriter does. Why is this happening?

package main

import (

type ConsoleWriter struct{}
type BrokenConsoleWriter struct{}

func (b BrokenConsoleWriter) Write(p []byte) (n int, err error) {
    bytesWritten, error := fmt.Println(string(p))
    return bytesWritten, error

func (c ConsoleWriter) Write(p []byte) (n int, err error) {
    return len(p), nil

func main() {
    const url = "https://www.nytimes.com/"
    resp, err := http.Get(url)
    if err != nil {
        fmt.Println("Some error occurred:", err)

    //io.Copy(ConsoleWriter{}, resp.Body)
    io.Copy(BrokenConsoleWriter{}, resp.Body)

>Solution :

The Write() method is to implement io.Write() which documents that:

Write writes len(p) bytes from p to the underlying data stream. It returns the number of bytes written from p (0 <= n <= len(p)) and any error encountered that caused the write to stop early. Write must return a non-nil error if it returns n < len(p). Write must not modify the slice data, even temporarily.

Implementations must not retain p.

So your Write() method must report how many bytes you processed from the p slice passed to you. Not how many bytes you generate to some other source.

And this is the error with your BrokenConsoleWriter.Write() implementation: you don’t report how many bytes you process from p, you report how many bytes fmt.Prinln() actually writes. And since fmt.Prinln() also prints a newline after printing its arguments, the value it returns will surely be not valid for BrokenConsoleWriter.Write().

Note that fmt.Prinln() with a single string argument will write out that string and append a newline, which on unix systems is a single character \n, and \r\n on Windows. So on unix systems you also get a correct behavior if you subtract 1 from its return value:

func (b BrokenConsoleWriter) Write(p []byte) (n int, err error) {
    bytesWritten, error := fmt.Println(string(p))
    return bytesWritten - 1, error

Also note that the input is already formatted into lines, so you inserting newlines "randomly" by using fmt.Prinln() may even result in an invalid document. Do use fmt.Print() instead of fmt.Println():

func (b BrokenConsoleWriter) Write(p []byte) (n int, err error) {
    bytesWritten, error := fmt.Print(string(p))
    return bytesWritten, error

But the correctness of this solution still depends on the implementation of fmt.Print(). The correct solution is to report len(p) because that’s what happened: you processed len(p) bytes of the input slice (all of it).

Leave a ReplyCancel reply