Go Structs and Methods
Now it’s time to review OOP in Go.
Go OOP
Go only supports encapsulation, but it doesn’t support inheritance or polymorphism, which is not necessarily a bad thing, as it removes unnecessary confugions.
There’s no class in Go, there’s on struct.
Define a struct
Let’s declare a very common data structure treeNode with struct. It should have the following elements:
- value
- left, right, which are pointers
1 | type treeNode struct { |
To create a treeNode type of data
1 | func main(){ |
Here root is a treeNode type data with value of 0, left and right pointing to nil. The outputs will be:
1 | {0 <nil> <nil>} |
To assign values, left and right to the root treeNode,
1 | root = treeNode{value: 3} |
Note left and right are pointers, so they need to use & to get the node address.
We could also create treeNode with new method.
1 | root.right.left = new(treeNode) |
See how simple and elegant to use Go pointers, we could use . directly after a pointer pointing to another pointer.
- Use
.to access members (which can be an address, a value or the struct itself)
Slices of Structs
We can build a slice of structs
1 | nodes := []treeNode{ |
The outputs will be:
1 | [{3 <nil> <nil>} {0 <nil> <nil>} {6 <nil> 0xc0000a6018}] |
Methods
Go doesn’t have generator functions. We could use factory functions alternatively as generator functions.
1 | func createNode(value int) *treeNode { |
We could call the createNode function
1 | func main() { |
The outputs will be:
1 | {3 0xc0000a6018 0xc0000a6030} |
Go has automatic garbage collector, so we don’t need to know if the struct is on stack or heap.
Define struct method
Unlike other OOP languages, in Go the struct method is defined outside the struct block.
1 | func (node treeNode) print(){ |
We need to pass node treeNode as the parameter, which is similar to self in Python or this in Java.
To invoke it, we can just call directly:
1 | root.print() |
The output will be
1 | 3 |
We can define a “setter”
1 | func (node treeNode) setValue(value int){ |
Then invoke it:
1 | root.right.left.print() |
The outputs will be:
1 | 3 |
Because Go functions passes value not address, so the “setter” won’t work as expected. Instead, refactor it as
1 | func (node *treeNode) setValue(value int){ |
Re-invoking the functions, the outputs will be:
1 | 3 |
In-Order Traverse
To traverse the tree:
1 | func (node *treeNode) traverse(){ |
Invoke traverse in main, we will get the outputs:
1 | 0 |
Passing-Value vs. Passing-Pointer
- If we want to change the content, we must pass pointers
- If the struct is too big, consider passing pointers as well
- Be consistent, if passing pointers, keep passing pointers