Exercise 1
Question
Create a struct named Person with three fields: FirstName and LastName of type string and Age of type int. Write a function called MakePerson that takes in firstName, lastName, and age and returns a Person. Write a second function MakePersonPointer that takes in firstName, lastName, and age and returns a *Person. Call both from main. Compile your program with go build -gcflags="-m". This both compiles your code and prints out which values escape to the heap. Are you surprised about what escapes?
Solution
The code is straightforward. The only difference between MakePerson and MakePersonPointer is the & before Person in the return statement in MakePersonPointer
type Person struct {
FirstName string
LastName string
Age int
}
func MakePerson(firstName, lastName string, age int) Person {
return Person{
FirstName: firstName,
LastName: lastName,
Age: age,
}
}
func MakePersonPointer(firstName, lastName string, age int) *Person {
return &Person{
FirstName: firstName,
LastName: lastName,
Age: age,
}
}
func main() {
p := MakePerson("Fred", "Williamson", 25)
fmt.Println(p)
p2 := MakePersonPointer("Wilma", "Fredson", 32)
fmt.Println(p2)
}
Compiling the code with -gcflags="-m" shows when values escape to the heap (it also shows when functions are inlined, which is a compiler optimization that's not covered by this book):
$ go build -gcflags="-m"
# github.com/learning-go-book-2e/ch06/exercise_solutions/ex1
./main.go:11:6: can inline MakePerson
./main.go:19:6: can inline MakePersonPointer
./main.go:28:17: inlining call to MakePerson
./main.go:29:13: inlining call to fmt.Println
./main.go:30:25: inlining call to MakePersonPointer
./main.go:31:13: inlining call to fmt.Println
./main.go:11:17: leaking param: firstName to result ~r0 level=0
./main.go:11:28: leaking param: lastName to result ~r0 level=0
./main.go:19:24: leaking param: firstName
./main.go:19:35: leaking param: lastName
./main.go:20:9: &Person{...} escapes to heap
./main.go:29:13: ... argument does not escape
./main.go:29:14: p escapes to heap
./main.go:30:25: &Person{...} escapes to heap
./main.go:31:13: ... argument does not escape
The &Person{} returned from MakePersonPointer escapes to the heap. Any time a pointer is returned from a function, the pointer is returned on the stack, but the value it points to must be stored on the heap.
Surprisingly, it also says that p escapes to the heap. The reason for this is that it is passed into fmt.Println. This is because the parameter to fmt.Println are ...any. The current Go compiler moves to the heap any value that is passed in to a function via a parameter that is of an interface type. (Interfaces are covered in Chapter 7.)