Go slice that doesn't grow past capacity
Tuesday 4 June 2024

Stevie Guarding as usual. Grunewald, Berlin.

A while ago I needed a go slice that keeps growing until hitting capacity. after that I need it to discard the first item and add the new one to the end, without growing its capacity.

append will grow Go slice if you tried to append an item when the slice is at full capacity.

So what I write is a function that checks if the slice is full. If so it’ll copy all items past the first item back to index 0 which overwrite the first item leaving the last item empty. then append the new item to the end as normal.

And as Go introduced generics we can use the function with a slice of any type.

1func limitedAppend[T any](c *[]T, i T) {
2	if len(*c) >= cap(*c) {
3		copy((*c)[:len(*c)-1],(*c)[1:])
4		*c = (*c)[:len(*c)-1]
5	}
6
7	*c = append(*c, i)
8}

A use case for me was collecting some information periodically and making sure I keep the last 1000 items only.

1s := make([]int, 0, 10) // this will have 0 items and grow until 10 items
2for i := 0; i < 100; i++ {
3  limitedAppend(&s, i)
4}
5fmt.Println(s) // prints [90 91 92 93 94 95 96 97 98 99]

Go playground: https://go.dev/play/p/0MpcHN7OSTF

Update 23-06-2024

Golang now has a ring and double linked list https://pkg.go.dev/container which can be used for the same purpose instead of copying, it’ll add an allocation overhead for each element, but adding/removing elements is in constant time.

See Also