Slice
A slice in Go is a dynamic array that can store elements of the same type. It can be indexed and its length queried, much like a fixed-size array. Unlike arrays, slices can grow or shrink as needed. When the capacity is exceeded, a new underlying array is allocated, and the data is transferred.
General syntax
list := []Type{}
Its default value is nil
or a nil slice
.
If you don't define size to an array, Go will handle it as a slice.
Here are some example how you can create a slice
type:
package main
import (
"fmt"
"reflect"
)
func main() {
// similar to defining an array, except without a predefined length
slice1 := []int{1, 2}
fmt.Println(reflect.TypeOf(slice1).Kind().String()) // slice
array := [6]int{2, 3, 5, 7, 11, 13} // that creates a fixed size array
// creating from array
var slice2 []int = array[1:4]
// creating by using make with a length of 5
slice3 := make([]int, 5)
// using make and defining capacity allocates an underlying array
// of size 10 and returns a slice of length 0 and capacity 10
slice4 := make([]int, 0, 10)
fmt.Println("slice1", slice1)
fmt.Println("slice2", slice2)
fmt.Println("slice3", slice3)
fmt.Println("slice4", slice4)
}
You can run the above code here (opens in a new tab).
Its default value is nil
or a nil slice
, as it's not possible to assign the nil
value of another type.
length := len(array)
The default length will be zero.
Individual elements can be accessed using indexing, similar to an array
.
item := slice[1]
Adding an Item to a nil
Slice in Go
In Go, a nil
slice is a slice that has been declared but not initialized with any values. Despite being nil
, you can still use the built-in append
function to add items to it. When you append an item to a nil
slice, Go automatically allocates memory for the slice and adds the item to it. As a result, the slice will no longer be nil
and will have a length of 1.
Here's an example to demonstrate this behavior:
package main
import (
"fmt"
)
func main() {
var list []int = nil // Declare a nil slice of int
list = append(list, 1) // Append an item to the nil slice
fmt.Println(list) // Output: [1]
}
In this example, list
is initially a nil
slice.
After appending the integer 1
to it, the slice becomes [1]
with a length of 1.
This demonstrates that the append
function can handle nil
slices gracefully, making it easy to work with slices that may not be initialized at the time of declaration.
Play with the example here (opens in a new tab)
slices
package
Go Team offers a Go package called slices
, which is remarkably useful for slice operations.
You can find the official doc here (opens in a new tab), I will review couple of the functions and discuss them.
Delete
function
The Delete function zeroes out the elements of a given slice within a specified interval and adjusts the slice's length accordingly. In the example below, I demonstrate that the capacity of the slice remains unchanged.
package main
import (
"fmt"
"Go.org/x/exp/slices"
)
func main() {
slice := []int{1, 2, 3, 4, 5, 6, 7}
fmt.Printf("before delete len=%d cap=%d\n", len(slice), cap(slice))
fmt.Println(slice)
slice = slices.Delete(slice, 0, 5)
fmt.Printf("after delete len=%d cap=%d\n", len(slice), cap(slice))
fmt.Println(slice)
}
Run the code here (opens in a new tab)
DeleteFunc
function
The DeleteFunc
function zeroes out the elements of a given slice if they meet a specified condition. It does not change the capacity of the slice.
package main
import (
"fmt"
"Go.org/x/exp/slices"
)
func main() {
slice := []int{1, 2, 3, 4, 5, 6, 7}
fmt.Printf("before delete len=%d cap=%d\n", len(slice), cap(slice))
fmt.Println(slice)
slice = slices.DeleteFunc(slice, func(i int) bool {
return i > 3
})
fmt.Printf("after delete len=%d cap=%d\n", len(slice), cap(slice))
fmt.Println(slice)
}
Play with the code here (opens in a new tab)
Clip
function
The Clip function trims any unused capacity from the given slice, reducing its size to match its current length. In contrast, the Delete and DeleteFunc functions do not modify the underlying array; they only zero out the values. These functions can be useful for managing the size of the underlying array without altering its capacity.
package main
import (
"fmt"
"Go.org/x/exp/slices"
)
func main() {
slice := []int{1, 2, 3, 4, 5, 6, 7}
fmt.Printf("before delete len=%d cap=%d\n", len(slice), cap(slice))
fmt.Println(slice)
slice = slices.DeleteFunc(slice, func(i int) bool {
return i > 3
})
fmt.Printf("after delete len=%d cap=%d\n", len(slice), cap(slice))
fmt.Println(slice)
slice = slices.Clip(slice)
fmt.Printf("after clip len=%d cap=%d\n", len(slice), cap(slice))
fmt.Println(slice)
}
Play with the code here (opens in a new tab)
Grow
function
The Grow
function increases the capacity of a slice by at least the specified value. The underlying array of the slice will typically expand when all its fields are populated and a new element needs to be appended. Growing the underlying array involves linear complexity, as it requires assigning each value to the new array. This function allows us to control when this performance impact occurs
package main
import (
"fmt"
"Go.org/x/exp/slices"
)
func main() {
slice := []int{1, 2, 3, 4, 5, 6, 7}
fmt.Printf("before grow len=%d cap=%d\n", len(slice), cap(slice))
fmt.Println(slice)
slice = slices.Grow(slice, 10)
fmt.Printf("after grow len=%d cap=%d\n", len(slice), cap(slice))
fmt.Println(slice)
}
Test the code here (opens in a new tab)