Assumptions: In go, all function arguments are passed by value. To get pass-by-reference semantics/performance, go programmers pass values by pointer. Go will still make a copy of these arguments, but its making a copy of a pointer which is sometimes more memory efficient than making a copy of the actual parameter.
Question: What is going on when you pass an interface? i.e., in a program like this
package main
import "fmt"
type Messages struct {
hello string
}
func main() {
sayHelloOne(Messages{"hello world"});
sayHelloTwo(&Messages{"hello world"});
sayHelloThree(Messages{"hello world"});
}
//go makes a copy of the struct
func sayHelloOne(messages Messages) {
fmt.Println(messages.hello)
}
//go makes a *pointer* to the struct
func sayHelloTwo(messages *Messages) {
fmt.Println(messages.hello)
}
//go --- ???
func sayHelloThree(messages interface{}) {
fmt.Println(messages.(Messages).hello)
}
What happens with the argument when a programmer calls the sayHelloThree
function? Is messages
being copied? Or is it a pointer to messages
that's copied? Or is there some weird-to-me deferring going on until messages
is cast?
The interface value is copied. The interface value includes an underlying type descriptor and the underlying value, which may be any type (pointer or otherwise) which satisfies the interface. Being "wrapped" in an interface has no impact on these semantics. You can see this in your own quoted code: you have to assert the type to a Messages
value, not a *Messages
pointer. If, on the other hand, you passed a *Messages
in as the parameter, that's what you'd get inside the function.
This is easily demonstrated experimentally:
m := Messages{"hello world"}
var mi interface{}
mi = m
m.hello = "wait what?"
fmt.Println(mi.(Messages).hello)
// hello world
Working playground example: https://play.golang.org/p/lnVzr79eUZF
Also, be careful with generalizations like "making a copy of a pointer which is much more memory efficient than making a copy of the actual parameter" - this is not universally true, and the change in semantics is often more important than the change in resource usage patterns. For example, a pointer is obviously less efficient when the value is smaller than an address on the operating architecture. A pointer can also force the value onto the heap rather than the stack, which - regardless of its impact on memory usage - increases GC pressure, because the GC gets to ignore values on the stack.