Mastering Slice Operations: Tips and Tricks
There is nothing so useless as doing efficiently that which should not be done at all.
— Peter Drucker
Medium Link: Slice Notions You Should Know in Golang | by Wesley Wei | Jul, 2024 | Programmer’s Career
Hello, here is Wesley, Today’s article is about slice in Go. Without further ado, let’s get started.💪
1. Array vs Slice
1.1 Declaration and Initialization
An array is a collection of elements of the same data type, and it needs to specify its length and element type when declared. It cannot dynamically expand, and its size will be determined at compile time.
1 | func main() { |
Arrays have limited use cases, and slices are more commonly used. A slice is a variable-length sequence of elements of the same type, which is based on an array type and provides a layer of abstraction. It is very flexible and supports automatic expansion.
1 | type slice struct { |
1 | func main() { |
You may be curious about the output of slice3 and slice4. This involves the expansion strategy, which will be discussed below.
1.2 Function Parameters
In Go, only value copying is used, so if you pass an array to a function and modify its elements in the function, it will not affect the original array.
However, slices are different. As mentioned earlier, when you pass a slice to a function, you are actually passing a copy of the array pointer, as well as the length and capacity. The pointer may point to the same array, so modifying the elements in the function may affect the original array.
1 | func modifySlice(s []string) { |
Of course, the word I use here is ’may‘, and the examples I give are ones that affect the original array. But if you change the pointer to the underlying array inside the function, such as expansion or copying, it will not affect the external original array.
The following sections will introduce expansion scenarios and distinguish between these situations. Please continue reading. Some complex? Or perhaps this is the price for slice’s greater flexibility and broader application scenarios.
2. Copying Large Slices or Small Slices
In the Go language, there is only value passing, as mentioned earlier:
1 | type slice struct { |
If a copy occurs, it essentially copies these three fields. The difference between large slices and small slices is that the values of len
and cap
are slightly larger for large slices, so the cost is similar.
3. Shallow and Deep Copying of Slices
1 | func main() { |
The examples above show that:
- Using
:=
or=
operator to copy a slice is shallow copying. - Using
[:]
indexing to copy a slice is also shallow copying. - Using Go’s built-in
copy()
function to copy a slice is deep copying.
LeetCode 47 can help you understand the use of copy()
:
1 | func permute(nums []int) [][]int { |
4. Slice Expansion Strategy
1 | … |
- The capacity of a new slice is at least twice or 1.25 times the old slice’s capacity, depending on whether the original slice’s capacity is less than 256 or not.
- In the later part of the source code, when expanding a slice, it will perform memory alignment, which is related to memory allocation strategies and is relatively complex, so we’ll ignore it for now.
- After expansion, the underlying array of the slice has changed.
5. Nil Slice, Empty Slice, Zero Slice
1 | package main |
- A nil slice has a length and capacity of 0, and its equality comparison with
nil
returnstrue
. - An empty slice also has a length and capacity of 0, but its equality comparison with
nil
returnsfalse
, because all empty slices’ data pointers point to the zerobase address.
1 | // base address for all 0-byte allocations |
1 | slice := make([]int, 5) // 0 0 0 0 0 |
- A zero slice has its underlying array elements all set to zero values or
nil
, and a slice created withmake
that has a length and capacity not equal to 0 is considered a zero slice.
6. Passing a Slice vs Passing a Pointer to a Slice
Let’s look at an example:
1 | package main |
If originalSlice
is:
1 | originalSlice := make([]int, 3, 4) |
The result will be:
1 | Original slice before modifySlice: [0 1 2] |
You may have a question about why Original slice after modifySlice
is [100 1 2]
, not [100 1 2 200]
. This is because its length is 3, so only three numbers are output.
If originalSlice
is:
1 | originalSlice := []int{0, 1, 2} |
The result will be:
1 | Original slice before modifySlice: [0 1 2] |
The result will be more complex. You can run the code and think about it first.
This example has several points that need to be noticed:
- Why is
0xc0000b2030
not equal to0xc0000ac000
? This is because expansion occurred, causing the underlying array address to change. The main function’s result is[100 1 2]
. - Why is
0xc0000b2060
not equal to0xc0000ac000
? This is also due to expansion. - But why does the main function’s address remain
0xc0000b2060
, and its output is[100 1 2 200]?
This is because we passed a copy of the original slice’s address to the function, which changed the address to point to a new slice, so this effect is global.
7. Iterating over a slice using range
1 | func main() { |
In Go before 1.22, when using the range
loop to iterate over a slice u
, the address of variable v
does not change, as shown in the example where it always remains 0xc000060020. Therefore, the output after copying is not intuitive. Of course, this unintuitive issue has been fixed in Go 1.22, see: Fixing For Loops in Go 1.22 - The Go Programming Language
8. Summary
If you want to master the basic usage of slices, just remember three points:
- Understand how to use
len
andcap
- Understand value copying in Go
- Know that a slice is backed by an array and has a strategy for resizing
9. References
Go Slices: usage and internals - The Go Programming Language
More Series Articles about You Should Know In Golang:
https://wesley-wei.medium.com/list/you-should-know-in-golang-e9491363cd9a
And I’m Wesley, delighted to share knowledge from the world of programming.
Don’t forget to follow me for more informative content, or feel free to share this with others who may also find it beneficial. it would be a great help to me.
Give me some free applauds, highlights, or replies, and I’ll pay attention to those reactions, which will determine whether or not I continue to post this type of article.
See you in the next article. 👋
中文文章: https://programmerscareer.com/zh-cn/golang-slice/
Author: Wesley Wei – Twitter Wesley Wei – Medium
Note: Originally written at https://programmerscareer.com/golang-slice/ at 2024-07-21 19:58. If you choose to repost or use this article, please cite the original source.
Comments