Similar to C:
- Point with
*
:var p *int
==int *p;
- Generate pointer (get address of) with
&
:i := 1
andp = &i
No pointer arithmetic.
Functions/methods accept both variables and pointers. The golden rule is:
- Pass pointers when function/method needs to modify the parameter.
When a variable is passed, the function/method gets a copy and the original copy is not modified. With pointers the underlying value is modified.
Go does not have classes. It has structs like C.
Exported field names need to be uppercase to be visible outside the defining package.
// 02.3-01-structs.go
package main
import "fmt"
type Student struct {
FirstName string
LastName string
}
func main() {
// Make an instance
studentOne := Student{"Ender", "Wiggin"}
// Now we can access fields
fmt.Println(studentOne.FirstName)
// We can just assign fields using names, anything not assigned will be
// initialized with "zero" as we have seen before
studentTwo := Student{FirstName: "Petra"}
// We will print "{Petra }" notice the space after Petra which is supposed
// to be the delimiter between the fields, LastName is nil because it is not
// given a value
fmt.Println(studentTwo)
// Can also make a pointer to a struct
p := &studentOne
// Now instead of *p.LastName (doesn't work) we can just use p.LastName
// fmt.Println((*p).LastName) will not work with error message: invalid indirect of p (type Student)
fmt.Println(p.LastName)
// Which is the same as
fmt.Println(studentOne.LastName)
// We can just create a pointer out of the blue
p2 := &Student{"Hercule", "Poirot"}
fmt.Println(p2)
}
Tour of Go says, we have to create a pointer to a struct to access fields while we can just do it directly as we saw in the code.
var a [10]int
== int a[10];
.
Arrays cannot be resized.
// 02.3-02-array.go
package main
import "fmt"
func main() {
var a [5]int
a[0] = 10
a[4] = 20
fmt.Println(a) // [10 0 0 0 20]
// Array can be initialized during creation
// characters[2] is empty
characters := [3]string{"Ender", "Pentra"}
fmt.Println(characters) // [Ender Pentra ]
}
Slice is a dynamic view of an array. Slices don't store anything by themselves, they reference an array. If we change something via the slice, the array is modified.
Think of slices as dynamic arrays. When a slice is created out of the blue, an underlying array is also initialized and can be modified by the slice.
// 02.3-03-slice1.go
package main
import "fmt"
func main() {
// Create an array of strings with 3 members
characters := [3]string{"Ender", "Petra", "Mazer"}
// Last index is exclusive
// allMembers []string := characters[0:3]
var allMembers []string = characters[0:3]
fmt.Println("All members", allMembers)
var lastTwo []string = characters[1:3]
fmt.Println("Last two members", lastTwo)
// Replace Mazer with Bean
fmt.Println("Replacing Mazer with Bean")
allMembers[2] = "Bean"
fmt.Println("All members after Bean swap", characters)
fmt.Println("Last two members after Bean swap", lastTwo)
}
We can create array and slice literals. Meaning we can just initialize them by their members instead of assigning a length and then add more members. If a slice literal is created, the underlying array is also created.
// 02.3-04-slice2.go
package main
import "fmt"
func main() {
// Slice literal of type struct, the underlying array is created automatically
sliceStruct := []struct {
a, b int
}{
{1, 2},
{3, 4},
{5, 6}, // need this comma in the end otherwise it will not work
}
fmt.Println(sliceStruct)
}
If a length is not specified during array creation, the result is a slice literal as seen above.
If we do not want to specify a length we can use [...]
.
// 02.3-05-slice3.go
package main
import "fmt"
func main() {
characters := [...]string{"Ender", "Petra", "Mazer"}
fmt.Println(characters)
}
Slices have length and capacity.
- Length is the current number of items in the slice. Returned by
len(slice)
. - Capacity is the maximum number of items in the slice. Returned by
cap(slice)
. Capacity is determined by the number of items in the original array from the start of the slice and not the size of array. For example if the slice starts from the second item (index 1) of an array, slice capacity islen(array)-1
. This ensures that the slice cannot go past the array.
In most cases, we do not care about capacity. Create slices and append to them.
// 02.3-06-slice4.go
package main
import "fmt"
func main() {
ints := [...]int{0, 1, 2, 3, 4, 5}
fmt.Println(ints)
slice1 := ints[2:6]
// len=4 and cap=4 (from 3rd item of the array until the end)
printSlice(slice1)
slice1 = ints[2:4]
// len=2 but cap will remain 4
printSlice(slice1)
}
// Copied from the tour
func printSlice(s []int) {
fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
}
To create dynamically-sized arrays use make
. make
creates a zero-ed array and returns a slice pointing to it.
slice1 := make([]int, 10)
creates an int array of length 10.slice2 := make([]int, 5, 10)
creates an int array of length 5 and capacity of 10.
We can append stuff to slices and it grows as needed:
slice1 = append(slice1, 1)
We can append multiple elements:
slice1 = append(slice1, 1, 2, 3)
In order to append one slice to another (obviously they should be of the same type), we have to use ...
as follows:
slice1 = append(slice1, slice2...)
append
is a variadic function, meaning it can an arbitrary number of arguments. By passing slice2...
, we are essentially passing each member of slice2
one by one to append
.
This is pretty useful later on when we want to append two byte slices together.
// 02.3-07-slice-append.go
package main
import "fmt"
func main() {
// Create a slice pointing to an int array
s1 := make([]int, 5)
fmt.Println(s1) // [0 0 0 0 0]
for i := 0; i < len(s1); i++ {
s1[i] = i
}
fmt.Println(s1) // [0 1 2 3 4]
s2 := make([]int, 3)
for i := 0; i < len(s2); i++ {
s2[i] = i
}
fmt.Println(s2) // [0 1 2]
s3 := append(s1, s2...)
fmt.Println(s3) // [0 1 2 3 4 0 1 2]
}
range
iterates over slices. It returns an index and a copy of the item stored at that index.
for index, value := range slice
value
is optional but index
is not. Ignore either with _
.
// 02.3-08-range.go
package main
import "fmt"
func main() {
characters := [3]string{"Ender", "Petra", "Mazer"}
for i, v := range characters {
fmt.Println(i, v)
}
// 0 Ender
// 1 Petra
// 2 Mazer
fmt.Println("-----------")
// Only using index
for i := range characters {
fmt.Println(i, characters[i])
}
fmt.Println("-----------")
// Ignoring index
for _, v := range characters {
// No non-elaborate way to get index here
fmt.Println(v)
}
// Ender
// Petra
// Mazer
}