Hello!

In this post we are going to explore what is arrays та slices в golang.

Arrays

Creation

An array in golang is created as follows [NUMBER_OF_ELEMENTS_IN_ARRAY]TYPE{ELEMENTS}. And allows you to store many objects of the same type

package main

import (
	"fmt"
)

func main() {
	students := [3]string{"John", "Lucy", "Alex"}
	fmt.Println(students)
}
$ go run main.go
[John Lucy Alex]

To not specify a number that will mean the number of elements in the array, you can specify .... In this case, go will automatically determine how many elements there are and create an array of this length.

package main

import (
	"fmt"
)

func main() {
	students := [...]string{"John", "Lucy", "Alex"}
	fmt.Println(students)
}

When we create an array, it is not necessary to specify the elements of array during creation. You can create an empty one and add elements later. But in this case, you need to specify how many elements there should be. Because if we specify ... then an array with a length of 0 will be created and we will not be able to add any elements to it.

package main

import (
	"fmt"
)

func main() {
	students := [...]string{}
	students[0] = "John"
	fmt.Println(students)
}

If we specify the number of elements but do not specify them during creation, then null values of this type will be used. For example, our array will be of type int, it will be filled with zeros.

package main

import (
	"fmt"
)

func main() {
	studentsGrades := [3]int{}
	fmt.Println(studentsGrades)
}
$ go run main.go
[0 0 0]

Change values

Now that we have an empty array, we can add values to it. This is done as follows: arrayName[INDEX] = VALUE. Indexes start from zero, if we need to add the first element, then its index will be equal to 0, the index of the second element will be equal to 1, and so on

package main

import (
	"fmt"
)

func main() {
	studentsGrades := [3]int{}
	studentsGrades[0] = 99
	studentsGrades[2] = 22
	fmt.Println(studentsGrades)
}
$ go run main.go
[99 0 22]

In this example, we specified the value for the first element and the third, and the second element remained unchanged.

Array length

In order to see the length of the array, you can use the len() method

package main

import (
	"fmt"
)

func main() {
	studentsGrades := [3]int{}
	fmt.Println(len(studentsGrades))
}
$ go run main.go
3

Copy array

An array can be copied by simply assigning it to another variable. Example

package main

import (
	"fmt"
)

func main() {
	studentsGrades := [3]int{1, 2, 3}
	b := studentsGrades
	fmt.Printf("Students grades: %v\n", studentsGrades)
	fmt.Printf("Array b: %v\n", b)
}

Let’s now try to change the value in the array b and print the value

package main

import (
	"fmt"
)

func main() {
	studentsGrades := [3]int{1, 2, 3}
	b := studentsGrades
	b[0] = 99
	fmt.Printf("Students grades: %v\n", studentsGrades)
	fmt.Printf("Array b: %v\n", b)
}
$ go run main.go
Students grades: [1 2 3]
Array b: [99 2 3]

As you can see, only the second array has changed, the first has remained unchanged. And that’s because we made a copy of the array. In order to pass the same array to another variable, and not its copy, you need to use &. In future posts, we will take a closer look at what it is, and now we will just try to use it

package main

import (
	"fmt"
)

func main() {
	studentsGrades := [3]int{1, 2, 3}
	b := &studentsGrades
	b[0] = 99
	fmt.Printf("Students grades: %v\n", studentsGrades)
	fmt.Printf("Array b: %v\n", b)
}
go run main.go
Students grades: [99 2 3]
Array b: &[99 2 3]

Slices

Creating

Slices are very similar to arrays, they are even created very similarly. The only thing is that in slices, we do not specify in advance how many elements will be in the slice.

package main

import (
	"fmt"
)

func main() {
	sliceOne := []int{1, 2, 3}
	fmt.Printf("Slice One: %v\n", sliceOne)
}
$ go run main.go
Slice One: [1 2 3]

You can also see the length of a slice using the len() method. But we also add the cap method to the output.

package main

import (
	"fmt"
)

func main() {
	sliceOne := []int{1, 2, 3}
	fmt.Printf("Len: %v, Capacity: %v\n", len(sliceOne), cap(sliceOne))
}
$ go run main.go
Len: 3, Capacity: 3

Append to slice

What does this mean? Slices are using arrays to store data. And capacity indicates the size of the array currently used by the slice.

The main difference is that we can expand slices (which we can’t do with arrays). For example, I create a slice of three elements, in this case len and cap will be equal to three. Let’s try to add the fourth element to the slice. For this, you need to use the append method.

package main

import (
	"fmt"
)

func main() {
	sliceOne := []int{1, 2, 3}
	fmt.Printf("Len: %v, Capacity: %v\n", len(sliceOne), cap(sliceOne))
	sliceOne = append(sliceOne, 22)
	fmt.Printf("Len: %v, Capacity: %v\n", len(sliceOne), cap(sliceOne))
}
$ go run main.go
Len: 3, Capacity: 3
Len: 4, Capacity: 6

Initially, our len and cap were equal to three. After adding one element, len increased by 1 and became 4, but cap doubled and became 6. Because it is not efficient for go to create a new array every time and copy data there, therefore go itself determines which size array to create in order not to take up a lot of memory, but also do not create a new array every time we add a new element to a slice

With append you can add many elements to a slice SLICE_NAME = append(SLICE_NAME, ELEM1, ELEM2, ELEM3). You can also add a slice to a slice. But it will not work if we specify a slice instead of an element in the append method.

package main

import (
	"fmt"
)

func main() {
	sliceOne := []int{1, 2, 3, 5, 10}
	sliceTwo := []int{11, 233, 34}
	sliceOne = append(sliceOne, sliceTwo)
	fmt.Printf("Slice one: %v\n", sliceOne)
	fmt.Printf("Slice two: %v\n", sliceTwo)
}
$ go run main.go
# command-line-arguments
./main.go:10:30: cannot use sliceTwo (variable of type []int) as type int in argument to append

In order to be able to add a slice, you need to unpack it, this can be done using ...

package main

import (
	"fmt"
)

func main() {
	sliceOne := []int{1, 2, 3, 5, 10}
	sliceTwo := []int{11, 233, 34}
	sliceOne = append(sliceOne, sliceTwo...)
	fmt.Printf("Slice one: %v\n", sliceOne)
	fmt.Printf("Slice two: %v\n", sliceTwo)
}
$ go run main.go
Slice one: [1 2 3 5 10 11 233 34]
Slice two: [11 233 34]

Also, unlike arrays, when we assign it to another variable, we do not create a copy, but transfer the slice itself

package main

import (
	"fmt"
)

func main() {
	sliceOne := []int{1, 2, 3}
	b := sliceOne
	b[1] = 999
	fmt.Printf("Slice one: %v\n", sliceOne)
	fmt.Printf("Slice b: %v\n", b)
}
$ go run main.go
Slice one: [1 999 3]
Slice b: [1 999 3]

Slice from slice

We can make a slice from an existing slice using indexes like ARRAY_NAME[first_index:last_index]. For example, we have a slice of 5 elements and we need to create a slice that will have only the last three elements, then this can be done as follows

package main

import (
	"fmt"
)

func main() {
	sliceOne := []int{1, 2, 3, 5, 10}
	sliceTwo := sliceOne[2:]
	fmt.Printf("Slice one: %v\n", sliceOne)
	fmt.Printf("Slice two: %v\n", sliceTwo)
}
$ go run main.go
Slice one: [1 2 3 5 10]
Slice two: [3 5 10]

sliceTwo := sliceOne[2:] we create a new slice that includes all elements starting from the second index to the last. Therefore, you do not need to specify anything after :. If necessary, you can specify the last element that will be included.

If we try to replace an element in the second slice, it will be replaced in the first, this is because both slices use the same array with data

package main

import (
	"fmt"
)

func main() {
	sliceOne := []int{1, 2, 3, 5, 10}
	sliceTwo := sliceOne[1:3]
	sliceTwo[0] = 222
	fmt.Printf("Slice one: %v\n", sliceOne)
	fmt.Printf("Slice two: %v\n", sliceTwo)
}
$ go run main.go
Slice one: [1 222 3 5 10]
Slice two: [222 3]

Create slice with make

The make(TYPE, LEN, CAP) method can be used to create slices. Capacity is an optional argument. If we use make without capacity, it will correspond to the same thing as we create an empty slice []int{}.

package main

import (
	"fmt"
)

func main() {
	sliceOne := make([]int, 5)
	fmt.Printf("Slice one len %v, capacity %v\n", len(sliceOne), cap(sliceOne))
}
$ go run main.go
Slice one len 5, capacity 5

If we know the required capacity, it can be specified immediately, and then go will not have to create new arrays and transfer data to them as objects are added

package main

import (
	"fmt"
)

func main() {
	sliceOne := make([]int, 5, 100)
	fmt.Printf("Slice one len %v, capacity %v\n", len(sliceOne), cap(sliceOne))
}
$ go run main.go
Slice one len 5, capacity 100

Video