Welcome To Golang By Example

Declare/Initialize/Create an array or slice in Go (Golang)

Overview

Similar to any other programming language, golang also has an array data structure. But in go, arrays behave little differently than other languages and also we have something called slice in golang which is like a reference to an array. Slice is more powerful and convenient to use than an array.  Slice, in fact, is more analogous to arrays in another programming language.

In this article, we will learn how to

Creating a slice

There are four ways of creating a slice

Let’s look at each of above method one by one.

Using the []<type>{} format

The most common way of declaring a slice is this

s := []int

It declares an empty of slice of 0 length and 0 capacity. We can also initialise the slice during declaration

s := []int{1,2}

It declares a slice of integers of length 2 and also the capacity of 2. The capacity will be equal to the actual slice elements specified. We also have two library functions provided by go which can be used to know the length and capacity of a slice.

Let’s see a small program which shows the above points

package main

import "fmt"

func main() {
    sample := []int{}
    fmt.Println(len(sample))
    fmt.Println(cap(sample))
    fmt.Println(sample)

    letters := []string{"a", "b", "c"}
    fmt.Println(len(letters))
    fmt.Println(cap(letters))
    fmt.Println(letters)
}

Output

0
0
[]

3
3
[a b c]

When the actual elements are not specified, then both length and capacity of the slice is zero. When actual elements are specified , both  length and capacity is equal to the number of actual elements specified.

Creating a slice from another slice or array

A slice can be created by re-slicing an exiting slice or array.

Create a slice from Array

The format for creating a new slice by re-slicing an existing array is

[n]sample[start:end]

The above operation will return a new slice from the array starting from index start to index end-1. So the element at index end is not included in the newly created slice. While re-slicing, both start, and the end index are optional.

Let’s see an example.

package main

import "fmt"

func main() {
    numbers := [5]int{1, 2, 3, 4, 5}

    //Both start and end
    num1 := numbers[2:4]
    fmt.Println("Both start and end")
    fmt.Printf("num1=%v\n", num1)
    fmt.Printf("length=%d\n", len(num1))
    fmt.Printf("capacity=%d\n", cap(num1))

    //Only start
    num2 := numbers[2:]
    fmt.Println("\nOnly start")
    fmt.Printf("num1=%v\n", num2)
    fmt.Printf("length=%d\n", len(num2))
    fmt.Printf("capacity=%d\n", cap(num2))

    //Only end
    num3 := numbers[:3]
    fmt.Println("\nOnly end")
    fmt.Printf("num1=%v\n", num3)
    fmt.Printf("length=%d\n", len(num3))
    fmt.Printf("capacity=%d\n", cap(num3))

    //None
    num4 := numbers[:]
    fmt.Println("\nOnly end")
    fmt.Printf("num1=%v\n", num4)
    fmt.Printf("length=%d\n", len(num4))
    fmt.Printf("capacity=%d\n", cap(num4))
}

Output

Both start and end
num1=[3 4]
length=2
capacity=3

Only start
num1=[3 4 5]
length=3
capacity=3

Only end
num1=[1 2 3]
length=3
capacity=5

Only end
num1=[1 2 3 4 5]
length=5
capacity=5

Notice in above example that

The num1 slice would look like

The newly created slices still refer the original array. To check this change element at any one of the index of the array and then reprint the slice

numbers[3] = 8
fmt.Printf("num1=%v\n", num2)
fmt.Printf("num3=%v\n", num3)
fmt.Printf("num4=%v\n", num4)

Here is the output:

num1=[3 8 5]
num3=[1 2 3 8]
num4=[1 2 3 8 5]

This proves that each of the new slice is still referring to the original array.

Create a slice from slice

Whatever we discussed about re-slicing from an array also applies here as well. See below example which illustrates same thin

package main

import "fmt"

func main() {
    numbers := []int{1, 2, 3, 4, 5}

    //Both start and end
    num1 := numbers[2:4]
    fmt.Println("Both start and end")
    fmt.Printf("num1=%v\n", num1)
    fmt.Printf("length=%d\n", len(num1))
    fmt.Printf("capacity=%d\n", cap(num1))

    //Only start
    num2 := numbers[2:]
    fmt.Println("\nOnly start")
    fmt.Printf("num1=%v\n", num2)
    fmt.Printf("length=%d\n", len(num2))
    fmt.Printf("capacity=%d\n", cap(num2))

    //Only end
    num3 := numbers[:3]
    fmt.Println("\nOnly end")
    fmt.Printf("num1=%v\n", num3)
    fmt.Printf("length=%d\n", len(num3))
    fmt.Printf("capacity=%d\n", cap(num3))

    //None
    num4 := numbers[:]
    fmt.Println("\nOnly end")
    fmt.Printf("num1=%v\n", num4)
    fmt.Printf("length=%d\n", len(num4))
    fmt.Printf("capacity=%d\n", cap(num4))
}

Output

Both start and end
num1=[3 4]
length=2
capacity=3

Only start
num1=[3 4 5]
length=3
capacity=3

Only end
num1=[1 2 3]
length=3
capacity=5

Only end
num1=[1 2 3 4 5]
length=5
capacity=5

Here also the newly created slices refer to the same underlying array that was being referred to by the original slice.  To check this change element at any one of the index of the original slice and then reprint all the newly created slices

numbers[3] = 8
fmt.Printf("num1=%v\n", num2)
fmt.Printf("num3=%v\n", num3)
fmt.Printf("num4=%v\n", num4)

Using the make function

make is a builtin function provided by go that can also be used to create a slice. Below is the signature of make function

func make([]{type}, length, capacity int) []{type}

Capacity is an optional parameter while creating slice using the make function. When capacity is omitted, the capacity of the slice is equal length specified for the slice. When using make function, behind the scenes go allocates an array equal to the capacity. All the elements of the allocated array are initialized with default zero value of the type. Let’s see a program illustrating this point.

package main

import "fmt"

func main() {
    numbers := make([]int, 3, 5)
    fmt.Printf("numbers=%v\n", numbers)
    fmt.Printf("length=%d\n", len(numbers))
    fmt.Printf("capacity=%d\n", cap(numbers))

    //With capacity ommited
    numbers = make([]int, 3)
    fmt.Println("\nCapacity Ommited")
    fmt.Printf("numbers=%v\n", numbers)
    fmt.Printf("length=%d\n", len(numbers))
    fmt.Printf("capacity=%d\n", cap(numbers))
}

Output

numbers=[0 0 0]
length=3
capacity=5

With Capacity Ommited
numbers=[0 0 0]
length=3
capacity=3

Using the new function

new is a builtin function provided by go that can also be used to create a slice. It is not a very popular way of creating a slice as make is much more flexible in terms of functionalities . It is not generally used and also using new function returns a pointer to nil slice. Let’s see an example. In below example we are using the dereferencing operator ‘*’ as new function returns a pointer to the nil slice

package main

import "fmt"

func main() {
    numbers := new([]int)
    fmt.Printf("numbers=%v\n", *numbers)
    fmt.Printf("length=%d\n", len(*numbers))
    fmt.Printf("capacity=%d\n", cap(*numbers))
}

Output

numbers=[]
length=0
capacity=0

Create an array

Both number of elements and actual elements are optional in the array declaration.

In below example, we see 4 ways of creating of an array

[2]int{1, 2}
[2]int{}
[...]int{2, 3}
[...]int{}

Let’s see a code example illustrating above points. Also please keep in mind that the builtin function len() can be used to calculate the length of an array. In below program we are using len() function to calculate the length of the array.

package main

import "fmt"

func main() {
    //Both number of elements and actual elements
    sample1 := [2]int{1, 2}
    fmt.Printf("Sample1: Len: %d, %v\n", len(sample1), sample1)

    //Only actual elements
    sample2 := [...]int{2, 3}
    fmt.Printf("Sample2: Len: %d, %v\n", len(sample2), sample2)

    //Only number of elements
    sample3 := [2]int{}
    fmt.Printf("Sample3: Len: %d, %v\n", len(sample3), sample3)

    //Without both number of elements and actual elements
    sample4 := [...]int{}
    fmt.Printf("Sample4: Len: %d, %v\n", len(sample4), sample4)
}

Output

Sample1: Len: 2, [1 2]
Sample2: Len: 2, [2 3]
Sample3: Len: 2, [0 0]
Sample4: Len: 0, []

Notice in the above example that for sample3 variable the actual elements are filled up with the default value of int which is 0.

It is also ok if the actual elements specified are less than the length of the array. The rest of the elements are filled up with the default value of the type specified. See the below example. The length of the array specified is 4 while only 2 actual elements are declared. Hence the remaining two elements are assigned value 0 which is the default zero value of an int.

package main

import "fmt"

func main() {
    sample := [4]int{5, 8}
    fmt.Printf("Sample: Len: %d, %v\n", len(sample), sample)
}

Output

Sample: Len: 4, [5 8 0 0]