Objects are struct values
A struct type is a blueprint, and an object (an instance) is an actual value made from it. Go has no new keyword in the sense of other languages, and creating an object takes no ceremony. There are three common ways, and the lesson's exercise uses the third.
Three ways to create one
Declare a variable
var rex Dog
This creates a Dog value named rex. Every field starts at its zero value: "" for strings, 0 for numbers, false for booleans. With the empty Dog of this chapter there are no fields yet, but the variable exists and is ready to use.
Write a struct literal
rex := Dog{}
A composite literal builds the value and := declares the variable, inferring its type. Once the struct has fields, you set them inside the braces: Dog{name: "Rex"}.
Take a pointer
rex := &Dog{}
Putting & in front of the literal gives you a pointer to a new Dog, with type *Dog. The built-in new(Dog) does the same: it returns a *Dog pointing at a zeroed value. Go does not make you free anything, because the garbage collector reclaims values that nothing refers to any more.
Values and pointers behave differently
A variable of type Dog holds the struct itself. Assigning it to another variable copies it. A variable of type *Dog holds the address of a struct, so copying the pointer gives two names for one object.
type Dog struct {
name string
}
func main() {
a := Dog{name: "A"}
b := a // a copy
b.name = "B"
fmt.Println(a.name, b.name) // A B
p := &a // a pointer to a
p.name = "Z"
fmt.Println(a.name) // Z
}
This program prints A B and then Z. Changing the copy left a alone, while changing through the pointer changed a. Notice that p.name works on a pointer without any dereferencing syntax: Go follows the pointer for you.
Separate declarations also give separate objects. var rex Dog and var buddy Dog, or two calls of &Dog{}, each make an independent value, and changing the fields of one does not touch the other.
The lesson's exercise
The exercise asks for three dogs named rex, buddy and luna. The code below them tests each one with != nil, and that test decides which form to use. A plain Dog value can never be compared with nil, because a struct is not a pointer, so the code would not compile. A *Dog can be, and a pointer produced by &Dog{} is never nil.
- Create
rexwithrex := &Dog{}. The literal allocates a newDog, and&gives its address. - Create
buddythe same way. Each literal allocates a separate value, independent ofrex. - Create
lunathe same way, which shows that one type can produce any number of values.
type Dog struct {}
func mainFunction() {
rex := &Dog{}
buddy := &Dog{}
luna := &Dog{}
if rex != nil {
fmt.Println("rex exists")
}
if buddy != nil {
fmt.Println("buddy exists")
}
if luna != nil {
fmt.Println("luna exists")
}
}
The output is one line per dog:
rex exists
buddy exists
luna exists
The nil checks are always true here, because nothing in the program can make these pointers nil. They are in the lesson only to show that the variables hold objects. In real code you check for nil on pointers that came from somewhere else, such as a lookup that may find nothing.
Common mistakes
- Comparing a struct value with
nil. Only pointers, slices, maps, channels, functions and interfaces can be nil. ADogvalue always exists. - Using a nil pointer.
var rex *Dogdeclares a pointer that is nil, with noDogbehind it. Reading a field through it stops the program with a nil pointer dereference panic. - Forgetting that assignment copies. Passing a
Dogto a function gives the function its own copy, so changes inside do not reach the caller. Pass a*Dogif the function should change the original. - Leaving a declared variable unused. Go will not compile a function with an unused local variable.
Key takeaways
- You create an object by declaring a variable or writing a composite literal.
- Fields start at their zero values.
&Dog{}andnew(Dog)give a*Dog, which can be compared withnil.- Copying a struct value copies its fields, while copying a pointer shares the object.