Optimize Memory Usage With slices.Clip … NOT!
Update: This is a rewrite of the original article beyond just cosmetic changes. The original conclusion was plain wrong, caused by insufficient research. The benefits of Clip() are much more trivial than it may seem.
What does slices.Clip do?
Slices in Go are dynamic arrays with a length and capacity. When you shorten a slice using [:n], the underlying array retains its capacity. The slices.Clip() function, on the other hand, sets a slice's capacity to its actual length.
So if you have, say, a slice with a capacity of 100M elements but a length of only 20 elements, slices.Clip() sets this slice's capacity down to 20.
Yay, this saves quite some memory. Or does it?
How slices.Clip works
One could think that slices.Clip() is implemented to allow the excess size to be eventually garbage collected. After all, if the capacity is reduced to the actual size, the slice cannot grow beyond this capacity, right?
Actually, slices.Clip()’s implementation is as trivial as it can get:
func Clip[S ~[]E, E any](s S) S {
return s[:len(s):len(s)]
}
(Source)
In fact, slices.Clip() is just a convenience wrapper over the slicing operation s[:len(s):len(s)], which sets the slice's internal len and cap fields to the current length of the slice.
That's it.
The underlying array remains untouched. If it's 100M elements long before clipping, it's 100M elements long afterwards.
Passing this slice to a function or returning it to the caller won't help to free memory either. Yes, Go has pass-by-value semantics, but a slice value is just a struct that contains a pointer to an underling array. Pass-by-value doesn't equal deep copying: while the pointer is passed or returned as a copy, the underlying array doesn't get copied. Hence, it'll not become a candidate for garbage collection.
Of what use is Clip() then?
How clipping can actually de-allocate memory
There is a scenario where Clip() can help to free memory. Maybe you guessed it already: It's got something to do with append().
