int. The keys of the elements of an array or slice are non-negative integers which mark the positions of these elements in the array or slice. The non-negative integer keys are often called indexes.
O(1), though, generally map element accesses are several times slower than array and slice element accesses. But maps have two advantages over arrays and slices:
[N]T
[]T
map[K]T where
T is an arbitrary type. It specifies the element type of a container type. Only values of the specified element type can be stored as element values of values of the container type.
N must be a non-negative integer constant. It specifies the number of elements stored in any value of an array type, and it can be called the length of the array type. This means the length of an array type is the inherent part of the array type. For example, [5]int and [8]int are two distinct array types.
K is an arbitrary comparable type. It specifies the key type of a map type. Most types in Go are comparable, incomparable types are listed here.
const Size = 32
type Person struct {
name string
age int
}
/* Array types */
[5]string
[Size]int
// Element type is a slice type: []byte
[16][]byte
// Element type is a struct type: Person
[100]Person
/* Slice types */
[]bool
[]int64
// Element type is a map type: map[int]bool
[]map[int]bool
// Element type is a pointer type: *int
[]*int
/* Map types */
map[string]int
map[int]bool
// Element type is an array type: [6]string
map[int16][6]string
// Element type is a slice type: []string
map[bool][]string
// Element type is a pointer type: *int8,
// and key type is a struct type.
map[struct{x int}]*int8
T{...}, where T denotes container type (except the zero values of slice and map types). Here are some examples:
// An array value containing four bool values.
[4]bool{false, true, true, false}
// A slice value which contains three words.
[]string{"break", "continue", "fallthrough"}
// A map value containing some key-value pairs.
map[string]int{"C": 1972, "Python": 1991, "Go": 2009}
// The followings slice composite literals
// are equivalent to each other.
[]string{"break", "continue", "fallthrough"}
[]string{0: "break", 1: "continue", 2: "fallthrough"}
[]string{2: "fallthrough", 1: "continue", 0: "break"}
[]string{2: "fallthrough", 0: "break", "continue"}
// The followings array composite literals
// are equivalent to each other.
[4]bool{false, true, true, false}
[4]bool{0: false, 1: true, 2: true, 3: false}
[4]bool{1: true, true}
[4]bool{2: true, 1: true}
[...]bool{false, true, true, false}
[...]bool{3: false, 1: true, true}
...s mean we want to let compilers deduce the lengths for the corresponding array values.
int, but it must be a non-negative constant representable as a value of type int. And if it is typed, its type must be a basic integer type.
var a uint = 1
var _ = map[uint]int {a : 123} // okay
// The following two lines fail to compile,
// for "a" is not a constant key/index.
var _ = []int{a: 100} // error
var _ = [5]int{a: 100} // error
A can be represented with the composite literal A{}. For example, the zero value of type [100]int can be denoted as [100]int{}. All elements stored in the zero value of an array type are zero values of the element type of the array type.
nil.
nil, including later to be introduced function, channel and interface types.
[]T{} represents a blank slice value (with zero elements) of slice type []T, it is different from []T(nil). The same situation is for map[K]T{} and map[K]T(nil).
package main
import "fmt"
func main() {
pm := &map[string]int{"C": 1972, "Go": 2009}
ps := &[]string{"break", "continue"}
pa := &[...]bool{false, true, true, false}
fmt.Printf("%T\n", pm) // *map[string]int
fmt.Printf("%T\n", ps) // *[]string
fmt.Printf("%T\n", pa) // *[4]bool
}
{...}.
// A slice value of a type whose element type is
// *[4]byte. The element type is a pointer type
// whose base type is [4]byte. The base type is
// an array type whose element type is "byte".
var heads = []*[4]byte{
&[4]byte{'P', 'N', 'G', ' '},
&[4]byte{'G', 'I', 'F', ' '},
&[4]byte{'J', 'P', 'E', 'G'},
}
var heads = []*[4]byte{
{'P', 'N', 'G', ' '},
{'G', 'I', 'F', ' '},
{'J', 'P', 'E', 'G'},
}
type language struct {
name string
year int
}
var _ = [...]language{
language{"C", 1972},
language{"Python", 1991},
language{"Go", 2009},
}
var _ = [...]language{
{"C", 1972},
{"Python", 1991},
{"Go", 2009},
}
type LangCategory struct {
dynamic bool
strong bool
}
// A value of map type whose key type is
// a struct type and whose element type
// is another map type "map[string]int".
var _ = map[LangCategory]map[string]int{
LangCategory{true, true}: map[string]int{
"Python": 1991,
"Erlang": 1986,
},
LangCategory{true, false}: map[string]int{
"JavaScript": 1995,
},
LangCategory{false, true}: map[string]int{
"Go": 2009,
"Rust": 2010,
},
LangCategory{false, false}: map[string]int{
"C": 1972,
},
}
var _ = map[LangCategory]map[string]int{
{true, true}: {
"Python": 1991,
"Erlang": 1986,
},
{true, false}: {
"JavaScript": 1995,
},
{false, true}: {
"Go": 2009,
"Rust": 2010,
},
{false, false}: {
"C": 1972,
},
}
nil identifier to check whether or not the slice or map value is a zero value.
package main
import "fmt"
func main() {
var a [16]byte
var s []int
var m map[string]int
fmt.Println(a == a) // true
fmt.Println(m == nil) // true
fmt.Println(s == nil) // true
fmt.Println(nil == map[string]int{}) // false
fmt.Println(nil == []int{}) // false
// The following lines fail to compile.
/*
_ = m == m
_ = s == s
_ = m == map[string]int(nil)
_ = s == []int(nil)
var x [16][]int
_ = x == x
var y [16]map[int]bool
_ = y == y
*/
}
len function to get the length of a container value, and use the built-in cap function to get the capacity of a container value. Each of the two functions returns an int typed result or an untyped result which default type is int, depending on whether or not the passed argument is a constant expression. As the capacity of any map value is unlimited, the built-in cap function doesn't apply to map values.
package main
import "fmt"
func main() {
var a [5]int
fmt.Println(len(a), cap(a)) // 5 5
var s []int
fmt.Println(len(s), cap(s)) // 0 0
s, s2 := []int{2, 3, 5}, []bool{}
fmt.Println(len(s), cap(s)) // 3 3
fmt.Println(len(s2), cap(s2)) // 0 0
var m map[int]bool
fmt.Println(len(m)) // 0
m, m2 := map[int]bool{1: true, 0: false}, map[int]int{}
fmt.Println(len(m), len(m2)) // 2 0
}
k stored in a container value v is represented with the element indexing syntax form v[k].
v[k], assume v is an array or slice,
k is a constant, then it must satisfy the requirements described above for the indexes in container composite literals. In addition, if v is an array, the k must be smaller than the length of the array.
k is a non-constant value, it must be a value of any basic integer type. In addition, it must be larger than or equal to zero and smaller than len(v), otherwise, a run-time panic will occur.
v is a nil slice, a run-time panic will occur.
v[k], assume v is a map, then k must be assignable to values of the element type of the map type, and
k is an interface value whose dynamic type is incomparable, a panic will occur at run time.
v[k] is used as a destination value in an assignment and v is a nil map, a panic will occur at run time.
v[k] is used to retrieve the element value corresponding key k in map v, then no panics will occur, even if v is a nil map. (Assume the evaluation of k will not panic.)
v[k] is used to retrieve the element value corresponding key k in map v, and the map v doesn't contain an entry with key k, v[k] results in a zero value of the element type of the corresponding map type of v. Generally, v[k] is viewed as a single-value expression. However, when v[k] is used as the only source value expression in an assignment, it can be viewed as a multi-value expression and result a second optional untyped boolean value, which indicates whether or not the map v contains an entry with key k.
package main
import "fmt"
func main() {
a := [3]int{-1, 0, 1}
s := []bool{true, false}
m := map[string]int{"abc": 123, "xyz": 789}
fmt.Println (a[2], s[1], m["abc"]) // retrieve
a[2], s[1], m["abc"] = 999, true, 567 // modify
fmt.Println (a[2], s[1], m["abc"]) // retrieve
n, present := m["hello"]
fmt.Println(n, present, m["hello"]) // 0 false 0
n, present = m["abc"]
fmt.Println(n, present, m["abc"]) // 567 true 567
m = nil
fmt.Println(m["abc"]) // 0
// The two lines fail to compile.
/*
_ = a[3] // index 3 out of bounds
_ = s[-1] // index must be non-negative
*/
// Each of the following lines can cause a panic.
_ = a[n] // panic: index out of range
_ = s[n] // panic: index out of range
m["hello"] = 555 // panic: assign to entry in nil map
}
type _slice struct {
elements unsafe.Pointer // referencing underlying elements
len int // length
cap int // capacity
}
len field of the direct part of a slice indicates the length of the slice, and the cap field indicates the capacity of the slice. The following picture depicts one possible memory layout of a slice value.
len to index cap (exclusive) don't belong to the elements of the slice. They are just some redundant element slots for the depicted slice, but they may be effective elements of other slices or another array.
append function. The result slice of an append function call may share starting elements with the base slice or not, depending on the capacity (and length) of the base slice and how many elements are appended.
append function call,
package main
import "fmt"
func main() {
m0 := map[int]int{0:7, 1:8, 2:9}
m1 := m0
m1[0] = 2
fmt.Println(m0, m1) // map[0:2 1:8 2:9] map[0:2 1:8 2:9]
s0 := []int{7, 8, 9}
s1 := s0
s1[0] = 2
fmt.Println(s0, s1) // [2 8 9] [2 8 9]
a0 := [...]int{7, 8, 9}
a1 := a0
a1[0] = 2
fmt.Println(a0, a1) // [7 8 9] [2 8 9]
}
m, the following line
m[k] = e
(k, e) into the map m if m doesn't contain an entry with key k, or modify the element value associated with k if m contains an entry with key k.
delete function which is used to delete an entry from a map. For example, the following line will delete the entry with key k from the map m. If the map m doesn't contain an entry with key k, it is a no-op, no panics will occur, even if m is a nil map.
delete(m, k)
package main
import "fmt"
func main() {
m := map[string]int{"Go": 2007}
m["C"] = 1972 // append
m["Java"] = 1995 // append
fmt.Println(m) // map[C:1972 Go:2007 Java:1995]
m["Go"] = 2009 // modify
delete(m, "Java") // delete
fmt.Println(m) // map[C:1972 Go:2009]
}
append function to append multiple elements into a base slice and result a new slice. The result new slice contains the elements of the base slice and the appended elements. Please note, the base slice is not modified by the append function call. Surely, if we expect (and often in practice), we can assign the result slice to the base slice to modify the base slice.
append function and the subslice feature introduced below together to achieve this goal. Slice element deletions and insertions will be demoed in the below more slice manipulations section. Here, the following example only shows how to use the append function.
append function:
package main
import "fmt"
func main() {
s0 := []int{2, 3, 5}
fmt.Println(s0, cap(s0)) // [2 3 5] 3
s1 := append(s0, 7) // append one element
fmt.Println(s1, cap(s1)) // [2 3 5 7] 6
s2 := append(s1, 11, 13) // append two elements
fmt.Println(s2, cap(s2)) // [2 3 5 7 11 13] 6
s3 := append(s0) // <=> s3 := s0
fmt.Println(s3, cap(s3)) // [2 3 5] 3
s4 := append(s0, s0...) // double s0 as s4
fmt.Println(s4, cap(s4)) // [2 3 5 2 3 5] 6
s0[0], s1[0] = 99, 789
fmt.Println(s2[0], s3[0], s4[0]) // 789 99 2
}
append function is a variadic function. It has two parameters, the second one is a variadic parameter.
.... We can learn how to call variadic functions from the the article after next.
s4 := append(s0, s0[0], s0[1], s0[2])
s1 := append(s0, []int{7}...)
s2 := append(s1, []int{11, 13}...)
... manner, the append function doesn't require the variadic argument must be a slice with the same type as the first slice argument, but their element types must be identical. In other words, the two argument slices must share the same underlying type.
append call at line 8 will allocate a new underlying memory segment for slice s1, for slice s0 doesn't have enough redundant element slots to store the new appended element. The same situation is for the append call at line 14.
append call at line 10 will not allocate a new underlying memory segment for slice s2, for slice s1 has enough redundant element slots to store the new appended elements.
s1 and s2 share some elements, s0 and s3 share all elements, and s4 doesn't share elements with others. The following picture depicted the statuses of these slices at the end of the above program.
append call allocate a new underlying memory segment for the result slice, the capacity of the result slice is compiler dependent. For the standard Go compiler, if the capacity of the base slice is small, the capacity of the result slice will be at least the double of the base slice, to avoid allocating underlying memory segments frequently when the result slice is used as the base slices in later possible append calls.
append call to append elements into the base slice. For example,
package main
import "fmt"
func main() {
var s = append([]string(nil), "array", "slice")
fmt.Println(s) // [array slice]
fmt.Println(cap(s)) // 2
s = append(s, "map")
fmt.Println(s) // [array slice map]
fmt.Println(cap(s)) // 4
s = append(s, "channel")
fmt.Println(s) // [array slice map channel]
fmt.Println(cap(s)) // 4
}
append function call can't be an untyped nil (up to Go 1.25).
make Function
make function to create map and slice values. The built-in make function can't be used to create array values.
make function can also be used to create channels, which will be explained in the article channels in Go later.
M is a map type and n is an integer, we can use the following two forms to create new maps of type M.
make(M, n)
make(M)
n entries without reallocating memory again. The second form only takes one argument, in which case a new empty map with enough space to hold a small number of entries without reallocating memory again. The small number is compiler dependent.
n may be negative or zero, in which case it will be ignored.
S is a slice type, length and capacity are two non-negative integers, length is not larger than capacity, we can use the following two forms to create new slices of type S. (The types of length and capacity are not required to be identical.)
make(S, length, capacity)
make(S, length)
make function call are initialized as the zero value (of the slice element type).
make function to create maps and slices:
package main
import "fmt"
func main() {
// Make new maps.
fmt.Println(make(map[string]int)) // map[]
m := make(map[string]int, 3)
fmt.Println(m, len(m)) // map[] 0
m["C"] = 1972
m["Go"] = 2009
fmt.Println(m, len(m)) // map[C:1972 Go:2009] 2
// Make new slices.
s := make([]int, 3, 5)
fmt.Println(s, len(s), cap(s)) // [0 0 0] 3 5
s = make([]int, 2)
fmt.Println(s, len(s), cap(s)) // [0 0] 2 2
}
new Function
new function to allocate a value of any type and get a pointer which references the allocated value. The allocated value is a zero value of its type. For this reason, it is a nonsense to use new function to create map and slice values.
new function. However, it is seldom to do this in practice, for it is more convenient to use composite literals to allocate arrays.
package main
import "fmt"
func main() {
m := *new(map[string]int) // <=> var m map[string]int
fmt.Println(m == nil) // true
s := *new([]int) // <=> var s []int
fmt.Println(s == nil) // true
a := *new([5]bool) // <=> var a [5]bool
fmt.Println(a == [5]bool{}) // true
}
package main
import "fmt"
func main() {
a := [5]int{2, 3, 5, 7}
s := make([]bool, 2)
pa2, ps1 := &a[2], &s[1]
fmt.Println(*pa2, *ps1) // 5 false
a[2], s[1] = 99, true
fmt.Println(*pa2, *ps1) // 99 true
ps0 := &[]string{"Go", "C"}[0]
fmt.Println(*ps0) // Go
m := map[int]bool{1: true}
_ = m
// The following lines fail to compile.
/*
_ = &[3]int{2, 3, 5}[0]
_ = &map[int]bool{1: true}[1]
_ = &m[1]
*/
}
package main
import "fmt"
func main() {
type T struct{age int}
mt := map[string]T{}
mt["John"] = T{age: 29} // modify it as a whole
ma := map[int][5]int{}
ma[1] = [5]int{1: 789} // modify it as a whole
// The following two lines fail to compile,
// for map elements can be modified partially.
/*
ma[1][1] = 123 // error
mt["John"].age = 30 // error
*/
// Accesses are okay.
fmt.Println(ma[1][1]) // 789
fmt.Println(mt["John"].age) // 29
}
package main
import "fmt"
func main() {
type T struct{age int}
mt := map[string]T{}
mt["John"] = T{age: 29}
ma := map[int][5]int{}
ma[1] = [5]int{1: 789}
t := mt["John"] // a temporary copy
t.age = 30
mt["John"] = t // overwrite it back
a := ma[1] // a temporary copy
a[1] = 123
ma[1] = a // overwrite it back
fmt.Println(ma[1][1], mt["John"].age) // 123 30
}
baseContainer is an array or slice):
baseContainer[low : high] // two-index form
baseContainer[low : high : max] // three-index form
baseContainer[low : high : cap(baseContainer)]
low, high and max indexes must satisfy the following relation requirements.
// two-index form
0 <= low <= high <= cap(baseContainer)
// three-index form
0 <= low <= high <= max <= cap(baseContainer)
low and high indexes can be both larger than len(baseContainer), as long as the above relations are all satisfied. But the two indexes must not be larger than cap(baseContainer).
baseContainer is a nil slice and all indexes used in the expression are zero. The result slice derived from a nil slice is still a nil slice.
high - low, and the capacity of the result derived slice is equal to max - low. The length of a derived slice may be larger than the base container, but the capacity will never be larger than the base container.
low index is equal to zero, it can be omitted, either for two-index or three-index forms.
high is equal to len(baseContainer), it can be omitted, but only for two-index forms.
max can never be omitted in three-index forms.
baseContainer[0 : len(baseContainer)]
baseContainer[: len(baseContainer)]
baseContainer[0 :]
baseContainer[:]
baseContainer[0 : len(baseContainer) : cap(baseContainer)]
baseContainer[: len(baseContainer) : cap(baseContainer)]
package main
import "fmt"
func main() {
a := [...]int{0, 1, 2, 3, 4, 5, 6}
s0 := a[:] // <=> s0 := a[0:7:7]
s1 := s0[:] // <=> s1 := s0
s2 := s1[1:3] // <=> s2 := a[1:3]
s3 := s1[3:] // <=> s3 := s1[3:7]
s4 := s0[3:5] // <=> s4 := s0[3:5:7]
s5 := s4[:2:2] // <=> s5 := s0[3:5:5]
s6 := append(s4, 77)
s7 := append(s5, 88)
s8 := append(s7, 66)
s3[1] = 99
fmt.Println(len(s2), cap(s2), s2) // 2 6 [1 2]
fmt.Println(len(s3), cap(s3), s3) // 4 4 [3 99 77 6]
fmt.Println(len(s4), cap(s4), s4) // 2 4 [3 99]
fmt.Println(len(s5), cap(s5), s5) // 2 2 [3 99]
fmt.Println(len(s6), cap(s6), s6) // 3 4 [3 99 77]
fmt.Println(len(s7), cap(s7), s7) // 3 4 [3 4 88]
fmt.Println(len(s8), cap(s8), s8) // 4 4 [3 4 88 66]
}
s7 and s8 are hosted on a different underlying memory segment than the other containers. The elements of the other slices are hosted on the same memory segment hosting the array a.
func f() []int {
s := make([]int, 10, 100)
return s[50:60]
}
50) is larger than the length (10) of s, which is allowed.
package main
type S []int
type A [2]int
type P *A
func main() {
var x []int
var y = make([]int, 0)
var x0 = (*[0]int)(x) // okay, x0 == nil
var y0 = (*[0]int)(y) // okay, y0 != nil
_, _ = x0, y0
var z = make([]int, 3, 5)
var _ = (*[3]int)(z) // okay
var _ = (*[2]int)(z) // okay
var _ = (*A)(z) // okay
var _ = P(z) // okay
var w = S(z)
var _ = (*[3]int)(w) // okay
var _ = (*[2]int)(w) // okay
var _ = (*A)(w) // okay
var _ = P(w) // okay
var _ = (*[4]int)(z) // will panic
}
package main
import "fmt"
func main() {
var s = []int{0, 1, 2, 3}
var a = [3]int(s[1:])
s[2] = 9
fmt.Println(s) // [0 1 9 3]
fmt.Println(a) // [1 2 3]
_ = [3]int(s[:2]) // panic
}
copy Function
copy function to copy elements from one slice to another, the types of the two slices are not required to be identical, but their element types must be identical. In other words, the two argument slices must share the same underlying type. The first parameter of the copy function is the destination slice and the second one is the source slice. The two parameters can overlap some elements. copy function returns the number of elements copied, which will be the smaller one of the lengths of the two parameters.
copy function to copy elements between two arrays or between an array and a slice.
package main
import "fmt"
func main() {
type Ta []int
type Tb []int
dest := Ta{1, 2, 3}
src := Tb{5, 6, 7, 8, 9}
n := copy(dest, src)
fmt.Println(n, dest) // 3 [5 6 7]
n = copy(dest[1:], dest)
fmt.Println(n, dest) // 2 [5 5 6]
a := [4]int{} // an array
n = copy(a[:], src)
fmt.Println(n, a) // 4 [5 6 7 8]
n = copy(a[:], a[2:])
fmt.Println(n, a) // 2 [7 8 7 8]
}
copy function can be used to copy bytes from a string to a byte slice.
copy function call can be an untyped nil value (up to Go 1.25).
for key, element = range aContainer {
// use key and element ...
}
for and range are two keywords, key and element are called iteration variables. If aContainer is a slice or an array (or an array pointer, see below), then the type of key must be built-in type int.
= can be a short variable declaration sign :=, in which case the two iteration variables are both two new declared variables which are only visible within the for-range code block body, if aContainer is a slice or an array (or an array pointer), then the type of key is deduced as int.
for loop block, each for-range loop block creates two code blocks, an implicit one and an explicit one which is formed by using {}. The explicit one is nested in the implicit one.
for loop blocks, break and continue statements can also be used in for-range loop blocks,
package main
import "fmt"
func main() {
m := map[string]int{"C": 1972, "C++": 1983, "Go": 2009}
for lang, year := range m {
fmt.Printf("%v: %v \n", lang, year)
}
a := [...]int{2, 3, 5, 7, 11}
for i, prime := range a {
fmt.Printf("%v: %v \n", i, prime)
}
s := []string{"go", "defer", "goto", "var"}
for i, keyword := range s {
fmt.Printf("%v: %v \n", i, keyword)
}
}
for-range code block syntax has several variants:
// Ignore the key iteration variable.
for _, element = range aContainer {
// ...
}
// Ignore the element iteration variable.
for key, _ = range aContainer {
element = aContainer[key]
// ...
}
// The element iteration variable is omitted.
// This form is equivalent to the last one.
for key = range aContainer {
element = aContainer[key]
// ...
}
// Ignore both the key and element iteration variables.
for _, _ = range aContainer {
// This variant is not much useful.
}
// Both the key and element iteration variables are
// omitted. This form is equivalent to the last one.
for range aContainer {
// This variant is not much useful.
}
m, then the following code is guaranteed to clear all entries (but the ones with keys as NaN) stored in the map m:
for key := range m {
delete(m, key)
}
clear builtin function, which may be used to clear all entries of a map, including those with keys as NaN.)
for loop block:
for i := 0; i < len(anArrayOrSlice); i++ {
// ... use anArrayOrSlice[i]
}
for-range loop block (whether = or := before range)
for key, element = range aContainer {...}
aContainer. Please note, only the direct part of aContainer is copied. The container copy is anonymous, so there are no ways to modify it.
aContainer is an array, then the modifications made on the array elements during the iteration will not be reflected to the iteration variables. The reason is that the copy of the array doesn't share elements with the array.
aContainer is a slice or map, then the modifications made on the slice or map elements during the iteration will be reflected to the iteration variables. The reason is that the clone of the slice (or map) shares all elements (entries) with the slice (or map).
aContainer will be assigned (copied) to the iteration variable pair at each iteration step, so the modifications made on the direct parts of the iteration variables will not be reflected to the elements (and keys for maps) stored in aContainer. (For this fact, and as using for-range loop blocks is the only way to iterate map keys and elements, it is recommended not to use large-size types as map key and element types, to avoid large copy burdens.)
package main
import "fmt"
func main() {
type Person struct {
name string
age int
}
persons := [2]Person {{"Alice", 28}, {"Bob", 25}}
for i, p := range persons {
fmt.Println(i, p)
// This modification has no effects on
// the iteration, for the ranged array
// is a copy of the persons array.
persons[1].name = "Jack"
// This modification has not effects on
// the persons array, for p is just a
// copy of a copy of one persons element.
p.age = 31
}
fmt.Println("persons:", &persons)
}
0 {Alice 28}
1 {Bob 25}
persons: &[{Alice 28} {Jack 25}]
...
// A slice.
persons := []Person {{"Alice", 28}, {"Bob", 25}}
for i, p := range persons {
fmt.Println(i, p)
// Now this modification has effects
// on the iteration.
persons[1].name = "Jack"
// This modification still has not
// any real effects.
p.age = 31
}
fmt.Println("persons:", &persons)
}
0 {Alice 28}
1 {Jack 25}
persons: &[{Alice 28} {Jack 25}]
package main
import "fmt"
func main() {
m := map[int]struct{ dynamic, strong bool } {
0: {true, false},
1: {false, true},
2: {false, false},
}
for _, v := range m {
// This following line has no effects on the map m.
v.dynamic, v.strong = true, true
}
fmt.Println(m[0]) // {true false}
fmt.Println(m[1]) // {false true}
fmt.Println(m[2]) // {false false}
}
for-range loop variant or the traditional for loop to iterate their elements. In the following example, the loop in function fa is much less efficient than the loop in function fb.
type Buffer struct {
start, end int
data [1024]byte
}
func fa(buffers []Buffer) int {
numUnreads := 0
for _, buf := range buffers {
numUnreads += buf.end - buf.start
}
return numUnreads
}
func fb(buffers []Buffer) int {
numUnreads := 0
for i := range buffers {
numUnreads += buffers[i].end - buffers[i].start
}
return numUnreads
}
for-range loop block (note the sign before before range is :=)
for key, element := range aContainer {...}
// forrange1.go
package main
import "fmt"
func main() {
for i, n := range []int{0, 1, 2} {
defer func() {
fmt.Println(i, n)
}()
}
}
$ gotv 1.21. run forrange1.go
[Run]: $HOME/.cache/gotv/tag_go1.21.8/bin/go run forrange1.go
2 2
2 2
2 2
$ gotv 1.22. run forrange1.go
[Run]: $HOME/.cache/gotv/tag_go1.22.1/bin/go run forrange1.go
2 2
1 1
0 0
// forrange2.go
package main
import "fmt"
func main() {
var m = map[*int]uint32{}
for i, n := range []int{1, 2, 3} {
m[&i]++
m[&n]++
}
fmt.Println(len(m))
}
$ gotv 1.21. run forrange2.go
[Run]: $HOME/.cache/gotv/tag_go1.21.8/bin/go run forrange2.go
2
$ gotv 1.22. run forrange2.go
[Run]: $HOME/.cache/gotv/tag_go1.22.1/bin/go run forrange2.go
6
package main
import "fmt"
func main() {
var a [100]int
// Copying a pointer is cheap.
for i, n := range &a {
fmt.Println(i, n)
}
// Copying a slice is cheap.
for i, n := range a[:] {
fmt.Println(i, n)
}
}
for-range loop is neither ignored nor omitted, then range over a nil array pointer will panic. In the following example, each of the first two loop blocks will print five indexes, however, the last one will produce a panic.
package main
import "fmt"
func main() {
var p *[5]int // nil
for i, _ := range p { // okay
fmt.Println(i)
}
for i := range p { // okay
fmt.Println(i)
}
for i, n := range p { // panic
fmt.Println(i, n)
}
}
package main
import "fmt"
func main() {
a := [5]int{2, 3, 5, 7, 11}
p := &a
p[0], p[1] = 17, 19
fmt.Println(a) // [17 19 5 7 11]
p = nil
_ = p[0] // panic
}
package main
import "fmt"
func main() {
pa := &[5]int{2, 3, 5, 7, 11}
s := pa[1:3]
fmt.Println(s) // [3 5]
pa = nil
s = pa[0:0] // panic
// Should this line execute, it also panics.
_ = (*[0]byte)(nil)[:]
}
len and cap functions. Nil array pointer arguments for the two functions will not produce panics.
var pa *[5]int // == nil
fmt.Println(len(pa), cap(pa)) // 5 5
clear function to clear map entries and reset slice elements
clear builtin function. This function is used to clear map entries and reset slice elements.
package main
import "fmt"
func main() {
s := []int{1, 2, 3}
clear(s)
fmt.Println(s) // [0 0 0]
a := [4]int{5, 6, 7, 8}
clear(a[1:3])
fmt.Println(a) // [5 0 0 8]
m := map[float64]float64{}
x := 0.0
m[x] = x
x /= x // x is NaN now
m[x] = x
fmt.Println(len(m)) // 2
for k := range m {
delete(m, k)
}
fmt.Println(len(m)) // 1
clear(m)
fmt.Println(len(m)) // 0
}
clear function can even delete map entries with keys as NaN.
memclr Optimization
memclr calls. Before Go 1.21, we can make use of these optimizations to reset array/slice elements and clear map entries.
len and cap Functions May Be Evaluated at Compile Time
len or cap function is an array or an array pointer value, then the call is evaluated at compile time and the result of the call is a typed constant with type as the built-in type int. The result can be bound to named constants.
package main
import "fmt"
var a [5]int
var p *[7]string
// N and M are both typed constants.
const N = len(a)
const M = cap(p)
func main() {
fmt.Println(N) // 5
fmt.Println(M) // 7
}
len and cap functions are evaluated at compile time. Please read the Go Details & Tips 101 book for such cases.
package main
import (
"fmt"
"reflect"
)
func main() {
s := make([]int, 2, 6)
fmt.Println(len(s), cap(s)) // 2 6
reflect.ValueOf(&s).Elem().SetLen(3)
fmt.Println(len(s), cap(s)) // 3 6
reflect.ValueOf(&s).Elem().SetCap(5)
fmt.Println(len(s), cap(s)) // 3 5
}
reflect.SetLen function must not be larger than the current capacity of the argument slice s. The second argument passed to the reflect.SetCap function must not be smaller than the current length of the argument slice s and larger than the current capacity of the argument slice s. Otherwise, a panic will occur.
map[K]struct{} to simulate a set type with element type K. The size of the map element type struct{} is zero, elements of values of such map types don't occupy memory space.
The Go 101 project is hosted on Github. Welcome to improve Go 101 articles by submitting corrections for all kinds of mistakes, such as typos, grammar errors, wording inaccuracies, description flaws, code bugs and broken links.
If you would like to learn some Go details and facts every serveral days, please follow Go 101's official Twitter account @zigo_101.
reflect standard package.sync standard package.sync/atomic standard package.