In Go, arrays and slices are both used to store sequences of elements, but they have important differences in structure, flexibility, and use cases.
Working with Arrays
Arrays in Go are similar to C/C++ where they are of a fixed length, and all members of the array are of the same data type. Sometimes this is referred to as being statically sized. Once it is created, you cannot change the size of the array.
Benefits:
- Fixed Size: Since arrays have a fixed length, they can be more memory-efficient for small, constant-size data.
- Predictable Memory Layout: Arrays allow memory to be allocated in a single block, which can improve performance when working with low-level code or systems that require precise memory control.
- Compile-time Checks: Because the size of an array is known at compile time, Go can perform bounds checking and help catch errors early.
Drawbacks:
- Inflexibility: The fixed length of arrays can be restrictive. If you need to store more elements than the initial size, you’d have to create a new array and copy data over, which is inefficient.
- Limited Use in Go: Arrays are generally less common in Go programs because slices (which are more flexible) offer more practical functionality.
To define an array, we do it as one might expect, given the similarities with C/C++, with a touch of Go flavor.
We can either define a size, or assign initial values, and define the size that way.
var arr1 [3]int // define an empty array of 3 elements
var arr2 = [3]int{1, 2, 3} // define an array with 3 elements, and five it a size
arr3 := [5]int{2, 4, 6, 8, 10} // define an array with 5 elements, without the var keyword
Notes that in each of these, we’ve specified a size. But you don’t have to. Go will infer the length of the array.
var arr1 = [...]int{1, 2, 3}
arr2 := [...]int{4, 6, 8}
Printing an Array
If you pass an array to a print function, it will print every element of the array, similar to how Python handles the printing of lists.
var arr1 = [...]int{1, 2, 3}
fmt.Println(arr1) // prints out [1 2 3]
Notice that unlike some languages, there are no commas between the elements, they are just separated by spaces.
Selecting a Single Element
You access a single element of an array, you use the index within square brackets just like most languages have you do.
var arr1 = [3]int{1, 2, 3}
arr1[1] = 37 // an almost perfect prime number
fmt.Println(arr1)
Working with a Slice
A slice in Go is a dynamically-sized, flexible view into the elements of an array. Slices are often used as they provide more versatile functionality.
Benefits:
- Dynamic Size: Slices can grow and shrink as needed, making them ideal for collections of unknown or variable size.
- Efficient Memory Use: Slices are backed by arrays but allow more flexible management of memory. When a slice grows beyond its current array, Go automatically creates a new underlying array with more space.
- Convenient Syntax: Slices are simpler to work with in Go, as they come with built-in functions like
append
for adding elements.
Drawbacks:
- Underlying Array Growth Cost: When a slice grows beyond the capacity of its underlying array, a new array is allocated, and the data is copied over, which can be costly in performance for large datasets.
- Shared Memory: Multiple slices can reference the same underlying array, which can lead to unintended side effects if one slice modifies the array’s elements, impacting other slices using the same array.
To create a slices, you don’t specify a length within the square brackets, similar to what we had before, as you can see here.
var arr1 []int // gives a slice
var arr2 = []int{1, 2, 3}
arr3 := []int{2, 4, 6, 8, 10}
You can also use the make command, to create a slice, and specify the elements and the capacity.
mySlice:= make([]int, 5, 10)
fmt.Printf("myslice1 = %v\n", mySlice)
fmt.Printf("length = %d\n", len(mySlice))
fmt.Printf("capacity = %d\n", cap(mySlice))
Append Elements To a Slice
You can append an element to a slice, but it’s not quite as simple as some languages, such as dynamic lists in Python or Vectors in C++ or Java.
You will need to use the append function, but it will return a new slice, with the old slice’s info, plus one or more new elements you wish to add.
// general form
slice_name = append(slice_name, element1, element2, ...)
// example
mySlice := []int{1, 3, 5, 7}
mySlice = append(mySlice, 2, 4, 6, 8)
fmt.Print(mySlice) // output is: [1 3 5 7 2 4 6 8]
Removing an Element from a Slice
You can remove an element from a slice, ironically by using the append()
function. Append creates the slice, based upon elements passed to it. If you don’t include a whole slice, you can create a portion, like you see here:
numbers := []int{10, 20, 30, 40, 50, 90, 60}
fmt.Println("Original Slice:", numbers)
index := 3 // element to remove
numbers = append(numbers[:index], numbers[index+1:]...)
fmt.Println("Slice after deleting element:", numbers)
Key Differences Between the Two
Feature | Array | Slice |
---|---|---|
Size | Fixed at compile-time | Dynamic, can grow and shrink |
Flexibility | Limited | Highly flexible, resizable |
Memory Allocation | Allocated in a single block | References an underlying array |
Syntax Simplicity | Requires explicit length | More concise, supports append function |
Usage in Practice | Rarely used for collections | Commonly used for collections |
Choosing Between Arrays and Slices
- Use arrays when you need a fixed-size, low-level, predictable memory structure, especially for performance-critical or memory-sensitive applications.
- Use slices in most cases, as they offer the flexibility and simplicity needed for typical programming tasks.
Commands that can Work with Both
There are several built in functions that work with both, such as len()
and cap()
. You will notice that these are not methods, but external commands. This helps keep the memory size of your arrays/slices smaller and thus more efficient when you look at the size of your overall app, especially when dealing wit large apps.
len()
is used to find the length of an array/slice. That is how many elements are in the variable.
Where cap()
is used to find the capacity. For an array it should be the same as the number of elements. However, for a slice, it may be more.
var sliceCapacity := cap(mySlice)
var arraySize := len(myArray)
Creating a Slice From an Array
You can also create a slice, from a part (or all) of an array.
To create your slice, you’ll specify your slice variable name, then set it equal to an array, where you define your low and high index values.
var mySlice = myArray[low:high] // generic form
var mySlice = myArray[1:4] // slice from index 1 to 3
var mySlice = myArray[:2] // missing low index implies 0 for the low index
var mySlice = myArray[5:] // missing high index implies len(a) for that high index
Looping Through and Array/Slice
You can loop through an array/slice using a standard for loop, or you can use the for each style loop.
// if you only need your element:
for _, e := range myArray {
// e is the element, you can use any variable you want
}
// if you only need the index
for i := range myArray {
}
Using Arrays and Slices in Go was originally found on Access 2 Learn