Structs help organize related data into a single entity.
Generally, structs are defined at the module level, and can be capitalized if they are to be exported:
package main
type item struct {
name string
price int
}
func main() {
...
}
name
and price
are referred to as fields of item
.
Two ways to declare structs using zero-value initialization:
...
func main() {
var waffle item
dog := item{}
fmt.Printf("%#+v\n%#+v\n", waffle, dog) // go doc fmt
}
There are two ways to declare a struct while initializing the fields of the struct:
...
func main() {
teddyBear := item{"teddy", 2}
fidgetSpinner := item{price: 2, name: "spinner"}
...
}
The first method doesn't use the field names, and the compiler implicitly knows that the field values are in order. If we include the field names, we can declare them in any order.
We can also get and set fields after struct declaration:
...
func main() {
teddyBear := item{"teddy", 2}
fmt.Println(teddyBear.price) // get
teddyBear.price = 3 // set
fmt.Println(teddyBear.price)
}
Nested structures are very common in go:
package main
import "fmt"
type node struct {
value int
next *node
}
func main() {
n1 := node{100, nil}
n2 := node{}
n1.next = &n2
fmt.Printf("%#+v\n%#+v\n", n1, n2)
}
We can also use structs in functions:
...
func main() {
n1 := node{100, nil}
n2 := node{}
n2.value = 45
n1.next = &n2
fmt.Println(nodeAndChildSum(n1, 2))
}
func nodeAndChildSum(n node, extra int) int {
return n.value + n.next.value + extra
}
Although this is valid go code, it is more common to capture struct related behavior in methods:
...
func main() {
n1 := node{100, nil}
n2 := node{45, nil}
n1.next = &n2
fmt.Println(n1.childSum(2))
}
func (n node) childSum(extra int) int {
return n.value + n.next.value + extra
}
Here we can see childSum
is defined on (n node)
, so now you can call x.childSum for any x that has been initialized as a node
.
Because structs are clunkier than basic types, it is common to see them created and passed around with pointers:
...
func main() {
n1 := &node{100, nil}
n2 := &node{45, nil}
n1.next = n2
fmt.Println(n1.childSum(2))
}
func (n *node) childSum(extra int) int {
// No dereferencing needed to access fields
return n.value + n.next.value + extra
}
A final note: structs can be defined at the module level or at the block level, but are generally defined at the module level. If defined in a block, they lose the ability to use methods.
Implement a circularly linked integer list.
- The
container
package may be a great reference for the exercise.
Go support composition over inheritance. Here is an example, or for more detail head over to Ardan Labs.
Another interesting post on empty structs.