Golang Slice Fundamentals and Best Practices

Author

Reads 237

Close-up of freshly sliced cherry tomatoes, highlighting their vibrant freshness.
Credit: pexels.com, Close-up of freshly sliced cherry tomatoes, highlighting their vibrant freshness.

Golang slices are a fundamental data structure in the Go programming language, and understanding their basics is crucial for efficient coding.

A Golang slice is a dynamically-sized, flexible view into an underlying array.

Slices are initialized using the `make` function, which allocates memory for the underlying array.

Golang slices are zero-indexed, meaning the first element is at index 0.

See what others are reading: Golang Copy Array

Syntax

A slice in Go can be declared in a simple way, like this:

To initialize the slice during declaration, use this:

The code above declares a slice of integers of length 3 and also the capacity of 3.

A slice has two important functions: len() and cap(). The len() function returns the length of the slice, which is the number of elements in the slice. The cap() function returns the capacity of the slice, which is the number of elements the slice can grow or shrink to.

A unique perspective: Golang Function Type

Array to List Conversion

Converting an array to a slice is straightforward. You can create a slice by slicing an array, as we saw in the example where myslice was created from arr1.

Credit: youtube.com, Golang | convert array into slice in Go / Golang

A slice can be created from any part of an array, not just the beginning. In the example, myslice started from the third element of arr1, which has value 12.

The length of the slice is determined by the starting index and the array it's created from. myslice has a length of 2, which is less than the length of arr1.

The capacity of a slice is the maximum number of elements it can hold without needing to reallocate memory. If myslice started from the first element of arr1, its capacity would be 6, which is the same as the length of arr1.

Slice Basics

A slice in Go is a reference to an array, and it's a fundamental data structure in the language. You can create a slice using a slice literal, which is similar to an array literal, but without specifying the size.

To create a slice using a slice literal, you can use the following syntax: `[]type{elements}`. For example, `[]int{1, 2, 3}` creates a slice of integers with the values 1, 2, and 3.

For your interest: Gcloud Api Using Golang

Credit: youtube.com, Go (Golang) Tutorial #5 - Arrays & Slices

You can also create a slice from an existing array, which is useful when you want to work with a subset of the array's elements. The syntax for this is `array[start:end]`, where `start` is the index of the first element to include and `end` is the index of the last element to include.

Here's an example: `arr := [3]int{1, 2, 3}; slice := arr[1:2]` creates a slice with the value 2.

Another way to create a slice is by using the `make()` function, which takes three parameters: `type`, `length`, and `capacity`. The `capacity` parameter is optional and defaults to the `length` if not specified.

Note that the `make()` function is often used to create an empty slice, which is a slice that contains an empty array reference.

Here are the three ways to create a slice:

  • Using a slice literal: `[]type{elements}`
  • Using an existing array: `array[start:end]`
  • Using the `make()` function: `make(type, length, capacity)`

Adding Elements

You can add elements to a slice in Go using the append function.

The append function appends elements to an existing slice, giving us dynamic array functionality.

Credit: youtube.com, How to append elements to slice in GoLang 1.20

You can use append to add elements to a slice like this: append(s, 42).

This will output: [42 0 0] because s was initialized to [0, 0, 0] with make([]int, 3) and then we appended 42 to that list.

Be careful when you use append to add elements to a list: do you want to start from index 0 or append them to the rest of the list?

As you add elements with append, you will eventually reach the end limit of the backing array.

When len equals cap, a new array will be allocated with a size double the previous capacity, and the pointer of the slice will point to the new allocated array.

After a certain number of elements, append will not double but grow with a lesser factor, and cap comes into play to tell append when it should allocate a new array for the slice.

Check this out: Golang Use .env File

Example Usage

You can create slices without specifying elements, resulting in a slice with a length and capacity of zero. This can be achieved using the []datatype{values} format.

Credit: youtube.com, Golang Slice Tricks Every Beginner Should Know

To create a slice with elements, you can use the make function, specifying the length and capacity of the slice. For example, make([]string, 3) creates a slice of strings with a length and capacity of 3.

Slices can be modified using the append function, which returns a new slice containing one or more new values. This function can be used to add elements to the end of a slice.

Here are some examples of how to use the append function:

Slices can also be copied using the copy function, which copies elements from one slice to another. This can be useful when you need to create a new slice that is identical to an existing slice.

In addition to these basic operations, slices support several other functions that make them richer than arrays. One of these functions is the slice operator, which allows you to extract a subset of elements from a slice. This can be achieved using the syntax slice[low:high].

For example, s[2:5] extracts a slice containing the elements s[2], s[3], and s[4].

Iterating and Checking

Credit: youtube.com, Tutorial 28 - Iterating Through The Slices In GO | Golang For Beginners

Iterating over a Go slice can be done in a few ways. You can use a for loop, which is the simplest method.

To iterate over a slice using a for loop, you can get the index and the element value. This can be useful for tasks that require both the position and value of each element.

Here are some ways to iterate over a slice using a for loop:

Iterating Over a List

Iterating over a list is a fundamental concept in programming, and there are several ways to do it. You can use a for loop, which is the simplest way to iterate over a list.

For example, you can use a for loop to iterate over a slice, as shown in the article section "Example 1". This method is straightforward and easy to understand.

Using range in a for loop is another way to iterate over a list. This method allows you to get the index and the element value, which can be useful in certain situations. For instance, if you need to perform an operation on each element in the list, you can use the index to keep track of the current position.

For more insights, see: Create a List of Structs Golang

Credit: youtube.com, Lists (Part 4) Iterating Over a List

Here are the different ways to iterate over a list:

  • Using a for loop with a range
  • Using a blank identifier in a for loop

Using a blank identifier in a for loop is useful when you don't need to know the index of the elements. This can make your code more concise and easier to read. For example, you can use a blank identifier in a for loop to iterate over a slice without getting the index value.

Checking for Empty

Always use the len function to test if a slice is empty, as checking for nil can have unintended effects.

You might be tempted to check for nil, but it's better to use len to avoid surprises like the one shown in the example.

Using len ensures you get the correct result, whether the slice is empty or not.

Checking for nil can lead to false negatives, making it a less reliable approach.

In the example, checking for nil resulted in an empty slice being reported as not empty, which can cause problems in your code.

You might enjoy: Golang Use Cases

Important Points

Credit: youtube.com, Golang Arrays & Slices (Interview Question)

A zero value slice in Go is a nil slice that doesn't contain any elements, with a capacity and length of 0.

Modifying a slice can be tricky because it's a reference type, meaning changes made to the slice will also affect the underlying array.

You can't compare slices using the == operator, but you can use the range for loop to match each element or the DeepEqual function instead.

Worth a look: T Golang

Why Capacity Matters

Capacity matters because it allows you to pre-allocate the internal array when creating a list, avoiding extra allocations.

This can be especially useful when you know in advance the number of elements you'll be inserting. By setting the capacity, you can allocate the right amount of space upfront, reducing the number of times the array needs to be resized.

For example, if you're creating a list that will hold 1000 elements, setting the capacity to 1000 will ensure that only one allocation is made, rather than allowing the array to grow and shrink as elements are added.

Top view of fresh pumpkin slices on a vibrant orange background, highlighting fall produce.
Credit: pexels.com, Top view of fresh pumpkin slices on a vibrant orange background, highlighting fall produce.

This approach can save time and improve performance, especially in scenarios where memory allocation is a concern.

By setting the capacity, you can also avoid the need for repeated allocations when creating a list from another list. This can be done by creating a slice with a length of 0 and a capacity equal to the desired size.

In this case, the slice will have space for the desired number of elements, and appending to it will not require any additional allocations.

For instance, if you create a slice with a length of 0 and a capacity of 2, the slice will be empty but its backing array will have space for 2 elements.

Important Points

You can create a nil slice in Go language that doesn't contain any elements. This type of slice has a length and capacity of 0.

Modifying a slice can be tricky because it's a reference type that can refer to an underlying array. If you change elements in the slice, the changes will also take place in the referenced array.

Workplace with modern laptop with program code on screen
Credit: pexels.com, Workplace with modern laptop with program code on screen

You can't compare two slices using the == operator, it will give you an error. Instead, use a range for loop to match each element or the DeepEqual function.

Multi-dimensional slices are similar to multidimensional arrays, but they don't contain their size. Sorting slices is also possible in Go language, using the sort package from the standard library.

Slice Growth and Allocation

A slice's capacity is the maximum number of elements it can hold before it needs to grow. By default, if you don't specify the third parameter in the slicing operation, the capacity is taken from the sliced slice or the length of the sliced array.

The capacity of a slice is not the same as the length of the underlying array. In fact, the capacity is the maximum number of elements the slice can hold before it needs to grow, which can be different from the length of the underlying array.

Credit: youtube.com, Go Class: 10 Slices in Detail

Go automatically creates a larger array, copies the elements over, and uses that new array as the slice's underlying array when the slice exceeds its current capacity. This is what happens when you keep adding elements to a slice and it surpasses its current capacity.

The growth rate of a slice's capacity is initially fast, doubling with each append operation. However, once the slice reaches a certain size, typically around 256, the growth slows down to 1.25 times the old capacity plus 192. This formula keeps the slice growing efficiently without wasting too much memory.

Here's a rough idea of how capacity grows from 0, depending on the type of slice:

How Grows

The capacity of a slice is the maximum number of elements it can hold before it needs to grow. Go will automatically create a larger array, copy the elements over, and use that new array as the slice's underlying array when the slice exceeds its current capacity.

Credit: youtube.com, Expanding a slice s size to prevent slice bounds out of range error

By default, if you don't specify the third parameter in the slicing operation, the capacity is taken from the sliced slice or the length of the sliced array. This means that the capacity of a slice can be set to go up to a specific index of the original array.

Go adjusts the growth rate once the slice reaches a certain size, typically around 256. At this point, the growth slows down, following a formula that equates to 1.25 * oldCap + 192.

This method works as long as the new length does not exceed the slice's capacity. If you keep adding elements to a slice and it surpasses its current capacity, Go will automatically create a larger array and use that new array as the slice's underlying array.

A table shows how capacity grows from 0 for different types of slices:

Doubling capacity indefinitely would lead to huge memory allocations as the slice gets larger.

How Is Allocated

Credit: youtube.com, Understanding Why Converting interface{} to Slice Causes Additional Heap Allocation in Go

In the context of slice growth, allocation is a crucial step that determines how resources are distributed among different slices.

A slice's allocation is directly tied to its growth rate, as seen in the comparison of slice A and slice B, where slice A's 20% growth rate led to a 10% allocation increase.

The allocation process is also influenced by the slice's age, with younger slices typically receiving a larger allocation to support their growth.

In the example of slice C, its 5% allocation was adjusted downward due to its slower growth rate compared to other slices.

The allocation is also based on the slice's current size, with larger slices receiving a smaller allocation percentage.

Curious to learn more? Check out: Golang Copy Slice

Emanuel Anderson

Senior Copy Editor

Emanuel Anderson is a meticulous and detail-oriented Copy Editor with a passion for refining the written word. With a keen eye for grammar, syntax, and style, Emanuel ensures that every article that passes through their hands meets the highest standards of quality and clarity. As a seasoned editor, Emanuel has had the privilege of working on a diverse range of topics, including the latest developments in Space Exploration News.

Love What You Read? Stay Updated!

Join our community for insights, tips, and more.