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
*/
}
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
}
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.