Golang bytes.Buffer-传递值问题

The below golang(go1.10.2) code will give an unexpected output

package main

import (
    "bytes"
    "fmt"
)

func main() {
    var b bytes.Buffer
    //Commenting the below line will fix the problem
    b.WriteString("aas-")
    fmt.Printf("Before Calling - \"%s\"
", b.String())
    b = makeMeMad(b)
    fmt.Printf("FinalValue - \"%s\"
", b.String())
}

func makeMeMad(b bytes.Buffer) bytes.Buffer {
    b.WriteString("xcxxcx asdasdas dasdsd asdasdasdasd")
    fmt.Printf("Write More - \"%s\"
", b.String())

    /*
        //This will fix the problem
        var newBuffer bytes.Buffer
        newBuffer.WriteString(b.String())
        return newBuffer
    */
    return b
}

Output

Before Calling - "aas-"
Write More - "aas-xcxxcx asdasdas dasdsd asdasdasdasd"
FinalValue - "aas-                                   "

I was expecting "aas-xcxxcx asdasdas dasdsd asdasdasdasd" in the last line of output. Could anyone please explain.

It is mentioned in Golang FAQ section as:

If an interface value contains a pointer *T, a method call can obtain a value by dereferencing the pointer, but if an interface value contains a value T, there is no useful way for a method call to obtain a pointer.

Even in cases where the compiler could take the address of a value to pass to the method, if the method modifies the value the changes will be lost in the caller. As an example, if the Write method of bytes.Buffer used a value receiver rather than a pointer, this code:

var buf bytes.Buffer
io.Copy(buf, os.Stdin)

would copy standard input into a copy of buf, not into buf itself

The error is because you're not passing the address of buffer inside makeMeMad function. That's why it has not override the original buffer inside main function. Pass address to the created buffer to append string to the existing buffer value.

package main

import (
    "bytes"
    "fmt"
)

func main() {
    var b bytes.Buffer
    //Commenting the below line will fix the problem
    b.WriteString("aas-")
    fmt.Printf("Before Calling - \"%s\"
", b.String())
    makeMeMad(&b)
    fmt.Printf("FinalValue - \"%s\"
", b.String())
}

func makeMeMad(b *bytes.Buffer) {
    b.WriteString("xcxxcx asdasdas dasdsd asdasdasdasd")
    fmt.Printf("Write More - \"%s\"
", b.String())

    /*
        //This will fix the problem
        var newBuffer bytes.Buffer
        newBuffer.WriteString(b.String())
        return newBuffer
    */
}

Playground Example

Or you can assign the returned buffer value to a new variable and you will get the updated buffer value.

package main

import (
    "bytes"
    "fmt"
)

func main() {
    var b bytes.Buffer
    //Commenting the below line will fix the problem
    b.WriteString("aas-")
    fmt.Printf("Before Calling - \"%s\"
", b.String())
    ab := makeMeMad(b)
    fmt.Printf("FinalValue - \"%s\"
", ab.String())
}

func makeMeMad(b bytes.Buffer) bytes.Buffer {
    b.WriteString("xcxxcx asdasdas dasdsd asdasdasdasd")
    fmt.Printf("Write More - \"%s\"
", b.String())

    /*
        //This will fix the problem
        var newBuffer bytes.Buffer
        newBuffer.WriteString(b.String())
        return newBuffer
    */
    return b
}

Working Code on Go Playground

Or you can create a global buffer to change the value inside the buffer whenever it is written by any function.

package main

import (
    "bytes"
    "fmt"
)

var b bytes.Buffer

func main() {
    //Commenting the below line will fix the problem
    b.WriteString("aas-")
    fmt.Printf("Before Calling - \"%s\"
", b.String())
    b := makeMeMad(b)
    fmt.Printf("FinalValue - \"%s\"
", b.String())
}

func makeMeMad(b bytes.Buffer) bytes.Buffer {
    b.WriteString("xcxxcx asdasdas dasdsd asdasdasdasd")
    fmt.Printf("Write More - \"%s\"
", b.String())

    /*
        //This will fix the problem
        var newBuffer bytes.Buffer
        newBuffer.WriteString(b.String())
        return newBuffer
    */
    return b
}

Playground Example

Under the hood bytes.Buffer contain among other unexported fields bootstrap array and buf slice. While buffer content is small slice points to internal array to avoid allocation. When you pass bytes.Buffer argument as value, function receives a copy. Slice is reference type, so this copy's slice continue to point on the original buffer's array. When you write to this copy's slice you actually write to original's bootstrap array, copy's array stay unchanged("aas-" in our case). Then you return this copy and you can print it. But when you assign it to variable containing original, bootstrap array first assigned("aas-") and then buf slice pointed on it. Bootstrap array is [64]byte, so you can use long string literals >64 in you code snippet and see things works as expected when buffer allocate buf slice. Also here simplified example trying to show why all this looks so contrintuitive.