A Go slice can expose more capacity than its current length. slices.Clip removes that spare capacity from the slice header, setting capacity to length without copying the elements into new storage.
The operation is deliberately narrow. It changes the range that a later append can reuse through that slice value, but it does not release the backing array or create an independent copy.
Clip is a full slice expression
The standard library defines Clip with this signature:
func Clip[S ~[]E, E any](s S) SIts result is equivalent to:
s[:len(s):len(s)]The third index in a full slice expression sets the resulting capacity. Clip packages that operation under a name that states the intent directly.
package main
import (
"fmt"
"slices"
)
func main() {
values := make([]int, 3, 8)
values[0], values[1], values[2] = 10, 20, 30
clipped := slices.Clip(values)
fmt.Println(len(clipped)) // 3
fmt.Println(cap(clipped)) // 3
}The length and visible elements are unchanged. Only the capacity recorded in the returned slice header is reduced.
No element copy occurs
Clipping does not allocate a replacement array. The returned slice still refers to the same backing array as the input.
values := make([]int, 2, 6)
values[0], values[1] = 4, 7
clipped := slices.Clip(values)
clipped[0] = 99
fmt.Println(values[0]) // 99Both slice values still reach the same existing elements. Clip therefore differs from slices.Clone, which copies elements into separate outer slice storage.
This distinction also means clipping alone is not a mechanism for reducing the memory occupied by a large backing allocation. A live clipped slice still keeps its backing array reachable.
Append crosses the clipped capacity boundary
The practical effect appears when code appends to the clipped value. Since len(clipped) == cap(clipped), appending another element cannot place that element in the unused tail of the existing capacity through that slice value. append must obtain enough storage for the extended result.
base := make([]int, 2, 6)
base[0], base[1] = 1, 2
clipped := slices.Clip(base)
extended := append(clipped, 3)
extended[0] = 50
fmt.Println(base[0]) // 1
fmt.Println(extended[0]) // 50The append produces a result whose storage can hold the extra element. Mutating that extended result does not rewrite base in this example because the clipped slice had no remaining capacity available for the append.
Without clipping, an append to a slice with spare capacity may reuse its backing array. That reuse is often desirable, but it can also make later writes visible through aliases that share the same array.
Existing aliases keep their own capacity
Capacity belongs to a slice value, not globally to its backing array. Clipping one slice does not alter another slice header that already refers to the same array.
values := make([]int, 2, 6)
alias := values
values = slices.Clip(values)
fmt.Println(cap(values)) // 2
fmt.Println(cap(alias)) // 6alias can still use its own spare capacity. A capacity boundary is therefore local to the clipped slice and values derived from it; it is not a permission system for the underlying storage.
That property matters at API boundaries. Returning a clipped slice can prevent callers from appending into capacity exposed through that returned value, but it cannot revoke access held by other aliases inside the program.
Nil state is preserved
A nil slice remains nil after clipping:
var values []int
values = slices.Clip(values)
fmt.Println(values == nil) // trueA non-nil empty slice remains non-nil. The operation changes capacity only as required to match length and preserves the input’s nil distinction.
Named slice types are also retained because the type parameter uses S ~[]E and returns S.
type IDs []int
ids := make(IDs, 2, 5)
ids = slices.Clip(ids)
fmt.Printf("%T %d\n", ids, cap(ids))
// main.IDs 2Capacity control is not storage reclamation
The name can suggest a stronger memory effect than the function actually provides. Clip removes unused capacity from a slice’s view; it does not shrink the allocation behind that view.
If a small result still points into a much larger backing array and retaining that array is undesirable, a copy into separate storage is the relevant operation. slices.Clone can provide that separation, subject to its shallow-copy semantics for reference-bearing elements.
slices.Clip instead addresses a different boundary: whether future growth through a particular slice value can consume its currently unused capacity. That makes it useful when capacity itself is part of the aliasing behavior a piece of code needs to control, without paying for an element copy solely to establish that append boundary.