Go 1.22 Unveiled: Embrace Simplicity with New Slice Features and Enhanced Functions!

Go 1.22 Unveiled: Embrace Simplicity with New Slice Features and Enhanced Functions!

Starting from the new version Go 1.22, there have been some additions and changes to the behavior of slices, making them more developer-friendly.

The following provides an explanation and overview of the adjustments made to functions such as Concat, Delete, DeleteFunc, Replace, Compact, CompactFunc, Insert, etc.

# New Addition: Concat Function

In previous Go versions, when concatenating two slices, developers often had to manually write code like the following:

1
2
3
4
5
6
7
func main() {
s1 := []string{"A", "B", "C"}
s2 := []string{"Deep-A", "Water fish", "A"}

s3 := append(s1, s2...)
fmt.Println(s3)
}

Output:

1
[A B C Deep-A Water fish A]

If such concatenation is frequently used in a Go project, developers might create a utility function, similar to the following:

1
2
3
4
5
6
7
8
9
func concatSlice[T any](first []T, second []T) []T {
n := len(first)
return append(first[:n:n], second...)
}
func main() {
s1 := []string{"A", "Deep-A"}
s2 := []string{"Water fish", "C", "A"}
s3 := concatSlice(s1, s2) fmt.Println(s3)
}

Output:

1
[A Deep-A Water fish C A]

If there’s a need to merge more than two slices, the implementation of this function becomes more complex.

However!

Starting from Go 1.22, a new Concat function has been introduced, making it easier to concatenate (join) multiple slices without the need to maintain a custom method.

The signature of the Concat function is as follows:

1
func Concat[S ~[]E, E any](slices ...S) S

Usage example:

1
2
3
4
5
6
7
8
9
10
11
import (
"fmt"
"slices"
)
func main() {
s1 := []string{"A"}
s2 := []string{"Deep-A", "Blue fish", "B"}
s3 := []string{"Lucky fish", "A"}
resp := slices.Concat(s1, s2, s3)
fmt.Println(resp)
}

This function is implemented based on generics, eliminating the need to implement it internally for each data type. It provides a convenient way for users, but it’s essential to ensure that the input slice types are consistent.

The internal implementation of the function is relatively straightforward, as shown in the following code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Concat returns a new slice concatenating the passed in slices.
func Concat[S ~[]E, E any](slices ...S) S {
size := 0
for _, s := range slices {
size += len(s)
if size < 0 {
panic("len out of range")
}
}
newslice := Grow[S](nil, size)
for _, s := range slices {
newslice = append(newslice, s...)
}
return newslice
}

It’s worth noting that a panic is triggered when size < 0 , but this seems to be more of a defensive programming measure. In general scenarios, this should not be triggered.

# Changes in Behavior for Delete and Related Functions

Starting from Go 1.22, the behavior of functions related to slices that reduce the slice segment/size has been adjusted. After the slice has been reduced, elements between the new length and the old length will be set to zero values.

This adjustment affects functions like Delete, DeleteFunc, Replace, Compact, CompactFunc, etc.

Here are some specific examples, divided into the old version (Go 1.21) and the new version (Go 1.22 and later).

# Delete-related Functions

Old version:

1
2
3
4
5
6
func main() {
s1 := []int{11, 12, 13, 14}
s2 := slices.Delete(s1, 1, 3)
fmt.Println("s1:", s1)
fmt.Println("s2:", s2)
}

Output:

1
2
s1: [11 14 13 14]
s2: [11 14]

In the new version, the program remains unchanged, but the output result has changed:

1
2
s1: [11 14 0 0]
s2: [11 14]

# Compact Function

Old version:

1
2
3
4
5
6
func main() {
s1 := []int{11, 12, 12, 12, 15}
s2 := slices.Compact(s1)
fmt.Println("s1:", s1)
fmt.Println("s2:", s2)
}

Output:

1
2
s1: [11 12 15 12 15]
s2: [11 12 15]

In the new version, the program remains unchanged, but the output result has changed:

1
2
s1: [11 12 15 0 0]
s2: [11 12 15]

# Replace Function

Old version:

1
2
3
4
5
6
func main() {
s1 := []int{11, 12, 13, 14}
s2 := slices.Replace(s1, 1, 3, 99)
fmt.Println("s1:", s1)
fmt.Println("s2:", s2)
}

Output:

1
2
s1: [11 99 14 14]
s2: [11 99 14]

In the new version, the program remains unchanged, but the output result has changed:

1
2
s1: [11 99 14 0]
s2: [11 99 14]

# Changes in Behavior for Insert Function, May Cause Panic

Old version:

1
2
3
4
5
6
func main() {
s1 := []string{"A", "Deep-A", "Water fish"}
s2 := slices.Insert(s1, 4)
fmt.Println("s1:", s1)
fmt.Println("s2:", s2)
}

Output:

1
2
s1: [A Deep-A Water fish]
s2: [A Deep-A Water fish]

In the new version, the program remains unchanged, but the output result has changed:

1
2
3
4
5
panic: runtime error: slice bounds out of range [4:3]

goroutine 1 [running]:
slices.Insert[...](0xc00010e0c0, 0x10100000010, 0x7ecd5be280a8)
...

In the above scenario, when using the slices.Insert function and not filling in a specific element to insert, it would run normally in the old version but would cause a panic in the new version.

Of course, if an element is filled in from the beginning, it would cause a panic in both the new and old versions. It can be considered as fixing a boundary value issue.

Go 1.22 Unveiled: Embrace Simplicity with New Slice Features and Enhanced Functions!

https://blog-1so.pages.dev/Go1.22_Unveiled_Embrace_Simplicity_with_New_Slice_Features_and_Enhanced_Functions/

author

Beck moulton

Published on

2024-05-01

Updated on

2024-05-31

license agreement