More types of Go: Pointers, structs, slices, and maps

§ Pointer to the

Go has Pointers. The pointer holds the memory address of the value.

The type *T is a pointer to a value of type T. Its zero value is nil.

var p *int
Copy the code

The & operator generates a pointer to its operand.

i := 42
p = &i
Copy the code

The * operator represents the underlying value to which the pointer points.

fmt.Println(*p) // Read I through the pointer p
*p = 21         // Set I by the pointer p
Copy the code

This is often referred to as an “indirect reference” or “redirect.”

Unlike C, Go has no pointer operations.

package main

import "fmt"

func main(a) {
	i, j := 42.2701

	p := &i
	fmt.Println(p)
	fmt.Println(*p)
	*p = 21
	fmt.Println(*p)

	p = &j
	*p /= 37
	fmt.Println(j)
}
Copy the code

Output:

0xc00008e000
42
21
73
Copy the code

§ The structure of the body

A struct is a set of fields.

package main

import "fmt"

type Vertex struct {
	x int
	y int
}

func main(a) {
	fmt.Println(Vertex{1.2})}Copy the code

Output:

{1} 2Copy the code

Structure field

Structure fields are accessed using a dot.

package main

import "fmt"

type Vertex struct {
	X int
	Y int
}

func main(a) {
	v := Vertex{1.2}
	v.X = 4
	fmt.Println(v.X)
}
Copy the code

Output:

4
Copy the code

Structure pointer

Struct fields can be accessed through the struct pointer.

If we have a pointer to the structure p, then we can access its field X through (*p).x. But that’s a long way to go, so the language also allows us to use implicit references, so just write p.X.

package main

import "fmt"

type Vertex struct {
	X int
	Y int
}

func main(a) {
	v := Vertex{1.2}
	p := &v
	p.X = 1e9
	fmt.Println(v)
}
Copy the code

Output:

{1000000000}Copy the code

Struct Literals

Structure grammar assigns a new structure by directly listing the values of the fields.

Use the Name: syntax to list only some fields. (The order of field names is irrelevant.)

The special prefix & returns a pointer to the structure.

package main

import "fmt"

type Vertex struct {
	X, Y int
}

var (
	v1 = Vertex{1.2}
	v2 = Vertex{X: 1}
	v3 = Vertex{}
	p0 = &Vertex{Y: 1, X: 2})func main(a) {
	fmt.Println(v1, v2, v3, *p0)
}
Copy the code

Output:

{1 2} {1 0} {0 0} {2 1}
Copy the code

§ An array of

Type [n]T represents an array with n values of type T.

expression

var a [10]int

The variable A is declared as an array of 10 integers.

package main

import "fmt"

func main(a) {
	var a [2]string
	a[0] = "Hello"
	a[1] = "World"
	fmt.Println(a[0], a[1])
	fmt.Println(a)

	primes := [6]int{2.3.5.7.11.13}
	fmt.Println(primes)
}
Copy the code

Output:

Hello World
[Hello World]
[2 3 5 7 11 13]
Copy the code

The length of an array is part of its type, so an array cannot be resized. This may seem like a limitation, but never mind, Go provides a much more convenient way to use arrays.

§ slice

Each array has a fixed size. Slicing, on the other hand, provides a dynamically sized, flexible view of the array elements. In practice, slices are more common than arrays.

Type []T represents a slice of element type T. (Array with no number of elements)

A slice is defined by two subscripts, an upper bound and a lower bound, separated by a colon:

a[low : high]

It chooses a half-open interval that includes the first element but excludes the last.

The following expression creates a slice that contains the elements in a with subscripts 1 through 3:

a[1:4]

package main

import "fmt"

func main(a) {
	primes := [6]int{2.3.5.7.11.13}

	var s []int = primes[1:4]
	fmt.Println(s)
}
Copy the code

Output:

[3 5 7]
Copy the code

Slices are like references to arrays

A slice does not store any data; it simply describes a segment of the underlying array.

Changing the element in the slice modifies the corresponding element in the underlying array.

These changes are observed by slices that share the underlying array with it.

package main

import "fmt"

func main(a) {
	names := [4]string{
		"A"."B"."C"."D",
	}
	fmt.Println(names)

	a := names[0:2]
	b := names[1:3]
	fmt.Println(a, b)

	b[0] = "XXX"
	fmt.Println(a, b)
	fmt.Println(names)
}
Copy the code

Output:

[A B C D]
[A B] [B C]
[A XXX] [XXX C]
[A XXX C D]
Copy the code

Slice literals

Slice grammar is similar to array grammar with no length.

This is an array grammar:

[3]bool{true, true, false}

This creates the same array as above, and then builds a slice that references it:

[]bool{true, true, false}

package main

import "fmt"

func main(a) {
	q := []int{2.3.5.7.11.13}
	fmt.Println(q)

	r := []bool{true.false.true.false}
	fmt.Println(r)

	s := []struct {
		i int
		b bool
	}{
		{2.true},
		{3.false},
		{5.true},
	}
	fmt.Println(s)
}
Copy the code

Output:

[2 3 5 7 11 13]
[true false true false]
[{2 true} {3 false} {5 true}]
Copy the code

Default behavior for slicing

When slicing, you can use its default behavior to ignore the upper and lower bounds.

The lower bound of a slice defaults to 0, and the upper bound is the length of the slice.

For an array of

var a [10]int

For example, the following slices are equivalent:

a[0:10]

a[:10]

a[0:]

a[:]

package main

import "fmt"

func main(a) {
	s := []int{2.3.5.7.11.13}

	s = s[1:4]
	fmt.Println(s)

	s = s[:2]
	fmt.Println(s)

	s = s[1:]
	fmt.Println(s)
}
Copy the code

Output:

[3 5 7]
[3 5]
[5]

Copy the code

Length and volume of slices

Slices have length and capacity.

  • The length of a slice is the number of elements it contains.

  • The size of a slice is counted from its first element to the end of its underlying array element.

The length and capacity of slice S can be obtained by len(s) and cap(s).

You can extend a slice’s length by re-slicing it, provided it has sufficient capacity. You can extend a slice’s length by re-slicing it, provided it has sufficient capacity.

To extend it beyond its capacity, there is a Runtime error.

package main

import "fmt"

func main(a) {
	s := []int{2.3.5.7.11.13}
	printSlice(s)

	// Cut the slice so that its length is 0
	s = s[:0]
	printSlice(s)

	// Expand its length
	s = s[:4]
	printSlice(s)

	// Discard the first two values
	s = s[2:]
	printSlice(s)

	// Expand its capacity outward
	s = s[2:10]
	printSlice(s)
}

func printSlice(s []int) {
	fmt.Printf("len=%d\tcap=%d\t %v\n".len(s), cap(s), s)
}
Copy the code

Output:

len=6	cap=6	 [2 3 5 7 11 13]
len=0	cap=6	 []
len=4	cap=6	 [2 3 5 7]
len=2	cap=4	 [5 7]
panic: runtime error: slice bounds out of range

goroutine 1 [running]:
main.main()
	/Users/example/go/tour/slice-len-cap/src.go:22 +0x483
exit status 2
Copy the code

Nil slice

The zero value of slice is nil.

A nil slice has a length and capacity of 0 and no underlying array.

package main

import "fmt"

func main(a) {
	var s []int
	fmt.Println(s, len(s), cap(s))
	if s == nil {
		fmt.Println("nil!")}}Copy the code

Output:

[] 0 0
nil!
Copy the code

Create slices with make

Slices can be created using the built-in make function, which is how dynamic arrays are created.

The make function allocates an array with zero elements and returns a slice that references it:

a := make([]int, 5) // len(a)=5

To specify its size (cap), pass a third argument to make:

b := make([]int.0.5) // len(b)=0, cap(b)=5

b = b[:cap(b)] // len(b)=5, cap(b)=5
b = b[1:]      // len(b)=4, cap(b)=4
Copy the code
package main

import "fmt"

func main(a) {
	a := make([]int.5)
	printSlice("a", a)

	b := make([]int.0.5)
	printSlice("b", b)

	c := b[:2]
	printSlice("c", c)

	d := c[2:5]
	printSlice("d", d)
}

func printSlice(s string, x []int) {
	fmt.Printf("%s: len=%d cap=%d %v\n", s, len(x), cap(x), x)
}
Copy the code

Output:

a: len=5 cap=5 [0 0 0 0 0]
b: len=0 cap=5 []
c: len=2 cap=5 [0 0]
d: len=3 cap=3 [0 0 0]
Copy the code

Section of a section

Slices can contain any type, even other slices.

package main

import (
	"fmt"
	"strings"
)

func main(a) {
	// Create a tic-tac-toe board
	board := [][]string{[]string{"_"."_"."_"},
		[]string{"_"."_"."_"},
		[]string{"_"."_"."_"}},// Two players take turns typing an X and an O
	board[0] [0] = "X"
	board[2] [2] = "O"
	board[1] [2] = "X"
	board[1] [0] = "O"
	board[0] [2] = "X"

	for i := 0; i < len(board); i++ {
		fmt.Printf("%s\n", strings.Join(board[i], ""))}}Copy the code

Output:

X  _  X
O  _  X
_  _  O
Copy the code

Appends elements to slices

Appending new elements to slices is a common operation, for which Go provides the built-in Append function. The built-in function documentation describes this function in detail.

func append(s []T, vs ... T) []T

The first argument s to Append is a slice of element type T, and the remaining values of type T are appended to the end of the slice.

The result of Append is a slice that contains all the elements of the original slice plus the newly added elements.

When s’s underlying array is too small to hold all the given values, it allocates a larger array. The returned slice points to the newly allocated array.

package main

import "fmt"

func main(a) {
	var s []int
	printSlice(s)

	// Add the element to an empty slice
	s = append(s, 0)
	printSlice(s)

	// The slice will grow as needed
	s = append(s, 1)
	printSlice(s)

	You can add more than one element at a time
	s = append(s, 2.3.4)
	printSlice(s)
}

func printSlice(s []int) {
	fmt.Printf("len=%d cap=%d %v\n".len(s), cap(s), s)
}
Copy the code

Output:

len=0 cap=0 []
len=1 cap=1 [0]
len=2 cap=2 [0 1]
len=5 cap=6 [0 1 2 3 4]
Copy the code

(To learn more about slicing, read the article Go Slices: Usage and Essence.)

Range

The range form of the for loop traverses slices or maps.

When a for loop is used to traverse a slice, two values are returned for each iteration. The first value is the index of the current element, and the second value is a copy of the element corresponding to that index.

package main

import "fmt"

var pow = []int{1.2.4.8.16.32.64.128}

func main(a) {
	for i, v := range pow {
		fmt.Printf("2**%d = %d\n", i, v)
	}
}
Copy the code

Output:

2 0 = 1 2 * * * * 1 = 2 * 2 * 2 = 4 * * 3 = 8 2 * * 4 = 16 * 2 * 5 = 32 * * 6 = 64 * 2 * 2 = 128Copy the code

Range (continue)

You can ignore the index or value by assigning it to _.

for i, _ := range pow for _, value := range pow

If you only need the index, ignore the second variable:

for i := range pow

package main

import "fmt"

func main(a) {
	pow := make([]int.10)
	for i := range pow {
		pow[i] = 1 << uint(i) // == 2 ** i
	}
	for _, value := range pow {
		fmt.Printf("%d\n", value)
	}
}
Copy the code

Output:

12 4 8 16 32 64 128 256 512Copy the code

Maps (map)

Mapping maps keys to values.

The zero value of the map is nil.

Nil maps have neither keys nor can they be added.

The make function returns a map of the given type and initializes it for standby.

package main

import "fmt"

type Vertex struct {
	Lat, Long float64
}

var m map[string]Vertex

func main(a) {
	fmt.Println(m)
	// m["A"] = Vertex{1.2, 3.4

	m = make(map[string]Vertex)
	m["B"] = Vertex{
		40.05.71.0,
	}
	fmt.Println(m["B"])}Copy the code

Output:

The map [] {} 40.05-71Copy the code

Grammar of mappings

The grammar of a map is similar to that of a structure, except that it must have a key name.

package main

import "fmt"

type Vertex struct {
	Lat, Long float64
}

var m = map[string]Vertex{
	"B" : Vertex{1.2},
	"C" : Vertex{3.4}},func main(a) {
	fmt.Println(m)
}
Copy the code

Output:

map[B:{1 2} C:{3 4}]
Copy the code

Grammar of mappings (cont.)

If the top-level type is just a type name, you can omit it in the elements of the grammar:

package main

import "fmt"

type Vertex struct {
	Lat, Long float64
}

var m = map[string]Vertex{
	"B": {1.2},
	"C": {3.4}},func main(a) {
	fmt.Println(m)
}
Copy the code

Output:

map[B:{1 2} C:{3 4}]
Copy the code

Modify the mapping

Insert or modify elements in map M:

m[key] = elem

Get the element:

elem = m[key]

Delete element:

delete(m, key)

Check whether a key exists by double assignment:

elem, ok = m[key]

Ok is true if key is in m; Otherwise, OK is false.

If key is not in the map, then ELEm is the zero value of the mapping element type.

Similarly, when a nonexistent key is read from a map, the result is the zero value of the mapped element type.

Note: If elem or OK is not already declared, you can use a short variable declaration:

elem, ok := m[key]

package main

import "fmt"

func main(a) {
	m := make(map[string]int)

	m["Answer"] = 42
	fmt.Println("The value:", m["Answer"])

	m["Answer"] = 48
	fmt.Println("The value:", m["Answer"])

	delete(m, "Answer")
	fmt.Println("The value:", m["Answer"])

	v, ok := m["Answer"]
	fmt.Println("The value:", v, "Present?", ok)
}
Copy the code

Output:

The value: 42
The value: 48
The value: 0
The value: 0 Present? false
Copy the code

Function value

Functions are also values. They can be passed like any other value.

Function values can be used as arguments or return values to a function.

package main

import (
	"fmt"
	"math"
)

func compute(fn func(float64.float64) float64) float64 {
	return fn(3.4)}func main(a) {
	hypot := func(x, y float64) float64 {
		return math.Sqrt(x*x + y*y)
	}
	fmt.Println(hypot(5.12))

	fmt.Println(compute(hypot))
	fmt.Println(compute(math.Pow))
}
Copy the code

Output:

May 13, 81Copy the code

Closure of a function

The Go function can be a closure. A closure is a function value that references a variable outside of its function body. The function can access and assign values to the variables it references; in other words, the function is “bound” to them.

package main

import "fmt"

func adder(a) func(int) int {
	sum := 0
	return func(x int) int {
		sum += x
		return sum
	}
}

func main(a) {
	pos, neg := adder(), adder()
	for i := 0; i < 10; i++ {
		fmt.Println(
			pos(i),
			neg(2 -*i),
		)
	}
}
Copy the code

Output:

0 0 1-2 3-6 6-12 10-20 15-30 21-42 28-56 36-72 45-90Copy the code

The 👆 function adder returns a closure. Each closure is bound to its respective sum variable.

Example: Fibonacci closures

package main

import "fmt"

// fibonacci is a function that returns
// a function that returns an int.

func fibonacci(a) func(a) int {
	i, j := 0.1
	return func(a) int {
		i, j = j, i + j
		return i
	}
}

func main(a) {
	f := fibonacci()
	for i := 0; i < 10; i++ {
		fmt.Println(f())
	}
}
Copy the code

Output:

1 1 2 3 5...Copy the code