Series

Here are all the posts in this series about the slices package.

Introduction

In the first post of this series, I discussed the binary search API from the slices package that is now part of the standard library with the release of version 1.21. In this post, I will share how the Clip, Clone, and Compact APIs work.

Clip

The Clip API can be used to remove unused capacity from a slice. This means reducing the capacity of the slice to match its length.

The code that will be reviewed for this example can be found here.

https://github.com/ardanlabs/gotraining/blob/master/topics/go/packages/slices/example2/example2.go

To start, a slice with some capacity is needed.

Listing 1

01 package main
02
03 import (
04	"fmt"
05	"slices"
06 )
07
08 func main() {
09	list := make([]string, 0, 10)
10	fmt.Printf("Addr(%x), Len(%d), Cap(%d)\n", &list, len(list), cap(list))

In listing 1 on line 05, you can see the import for the new slices package from the standard library. Then on line 09, a slice with a length of 0 and a capacity of 10 is constructed. Finally on line 10, the length and capacity of the slice is printed.

Figure 1

Figure 1 shows the slice value and underlying array after the construction. You can see the length of the size is 0 and the capacity is 10.

Next, a value needs to be appended to the slice, which will change the slice’s length.

Listing 2

12	list = append(list, "A")
13	fmt.Printf("Addr(%x), Len(%d), Cap(%d)\n", &list[0], len(list), cap(list))

In listing 2 on line 12, the letter A is appended to the slice and then the slice’s length and capacity is printed again.

Figure 2

Figure 2 shows the slice value and underlying array after the append operation. You can see the length changed to 1 and the capacity remained the same at 10.

Now the Clip API can be used to reduce the capacity of the slice to match the length.

Listing 3

15	list = slices.Clip(list)
16	fmt.Printf("Addr(%x), Len(%d), Cap(%d)\n", &list[0], len(list), cap(list))

In listing 3 on line 15, the Clip API is called and the length and capacity of the slice is printed one last time.

Figure 3

Figure 3 shows the slice value and underlying array after the clip operation. As expected, the length of the slice remained at 1 and the capacity changed to 1 as well.

Clone

The Clone API performs a shallow copy of each element in the backing array. A shallow copy means each element is duplicated, but any values that the element might be pointing to are not duplicated. Those values are shared between the two duplicated elements.

The code that will be reviewed for this example can be found here.

https://github.com/ardanlabs/gotraining/blob/master/topics/go/packages/slices/example3/example3.go

To start, a slice with some length and capacity is needed.

Listing 5

01 package main
02
03 import (
04	"fmt"
05	"slices"
06 )
07
08 func main() {
09	list := []int{1, 2, 3, 4, 5}
10	fmt.Printf("List: Addr(%x), %v\n", &list[0], list)

In listing 5 on line 09, a slice is constructed with five values and then the address of the first element of the backing array and all five elements are printed.

Figure 4

Figure 4 shows the slice value and underlying array after the construction. You can see the slice has a length of 5 and a capacity of 5.

Listing 6

12	list = slices.Clone(list)
13	fmt.Printf("Copy: Addr(%x), %v\n", &list[0], list)

In listing 6 on line 12, the Clone API is called and then the address of the first element of the backing array and all five elements are printed again.

Figure 5

Figure 5 shows what happened after the Clone API was called. A second slice value is constructed with its own backing array that is a shallow copy of the original slice. Notice the address of the first element is different.

Compact

The Compact API removes consecutive duplicate elements from a slice.

The code that will be reviewed for this example can be found here.

https://github.com/ardanlabs/gotraining/blob/master/topics/go/packages/slices/example4/example4.go

There are two versions of the Compact API:

One that takes a slice and returns a “compacted” version of it. One that takes a slice and a custom compare function.

To start, a slice with some length and capacity is needed.

Listing 8

01 package main
02
03 import (
04	"fmt"
05	"slices"
06 )
07
08 func main() {
09    list := []int{1, 1, 2, 2, 1, 1, 3, 3, 4, 5}
10	fmt.Printf("List: Addr(%x), %v\n", &list[0], list)

In listing 8 on line 09, a slice with ten elements having some consecutive duplicates is constructed. Then the address of the first element of the backing array and all ten elements are printed.

Figure 6

Figure 6 shows the slice value and underlying array after the construction. You can see the length and capacity of the slice if 10.

Listing 9

12	list = slices.Compact(list)
13	fmt.Printf("List: Addr(%x), %v\n", &list[0], list)

In listing 9 on line 12, the Compact API is called and the address of the first element of the backing array and all ten elements are printed again.

Figure 7

Figure 7 shows what happened after the Compact API was called. The slice value is still pointing to the original backing array and the length of the slice has changed from 10 to 6. All the elements associated with the length have been compacted to the front of the backing array.

There may be times when you need to compact slices that are based on a struct type and need to customize how the algorithm identifies a duplicate.

Listing 11

22 // compare needs to return true if the two values are the same.
23 func compare(b int, a int) bool {
24	return a == b
25 }

Listing 11 represents a custom compare function that can be used with the CompactFunc API. Thanks to generics, a function that takes two values of some type T (in this case an integer) can be declared, implemented, and then passed to the API.

Listing 12

15	list = []int{1, 1, 2, 2, 1, 1, 3, 3, 4, 5}
16	fmt.Printf("List: Addr(%x), %v\n", &list[0], list)
17
18	list = slices.CompactFunc(list, compare)
19	fmt.Printf("List: Addr(%x), %v\n", &list[0], list)

In listing 12 on line 15, the slice is constructed and printed again. Then on line 18, the CompactFunc API is called using the custom compare function.

Figure 8

Figure 8 illustrates how the a and b parameters are determined and passed to the compare function on each iteration the CompactFunc API performs under the covers.

Figure 9

Figure 9 shows how the previous compare removed the element that was a duplicate of the value 1 and compacted the remaining elements back. Then the algorithm compares the next two elements.

Figure 10

Figure 10 shows once again how the duplicate of value 2 is removed and the remaining elements are compacted once more.

Conclusion

You can see how cool this new slices package is by providing the Clip, Clone, and Compact APIs. In the next post, we will explore the Compare and CompareFunc APIs.

If you want to cheat and see all the examples I have prepared for future posts, check out the Go training repo.

https://github.com/ardanlabs/gotraining/tree/master/topics/go/packages/slices

Trusted by top technology companies

We've built our reputation as educators and bring that mentality to every project. When you partner with us, your team will learn best practices and grow along the way.

30,000+

Engineers Trained

1,000+

Companies Worldwide

12+

Years in Business