I upgraded a small service to Go 1.2 and immediately found a use for the new full slice expression. That sounds implausibly efficient for an upgrade, so naturally it followed a bug.

I had a helper that accepted a prefix of a work buffer and appended a marker:

func mark(p []byte) []byte {
	return append(p, '!')
}

buf := []byte("abcdef")
prefix := buf[:3]
marked := mark(prefix)
fmt.Printf("%s %s\n", marked, buf)

I expected a separate abc!. I got:

abc! abc!ef

The ordinary two-index slice preserves the unused capacity of the backing array. prefix had length 3 but capacity 6, so append reused the array and replaced d. The helper’s signature did not advertise permission to alter bytes beyond the visible slice, but capacity quietly granted exactly that permission.

Go 1.2 adds a third index:

prefix := buf[:3:3]

For a[low:high:max], the length is high-low and the capacity is max-low. Now the output is:

abc! abcdef

Because len(prefix) == cap(prefix), append must allocate another backing array. This is not an immutable slice. The helper can still change prefix[0], and anyone sharing the original array will see that change. The third index controls append’s room, nothing more.

It is also useful when dividing one large buffer among independent workers:

left := buf[0:3:3]
right := buf[3:6:6]

Appending to left can no longer wander into right. Before 1.2 I used append([]byte(nil), buf[:3]...), which copies eagerly and obscures why. The full slice expression is both cheaper and more precise when all I need is a capacity fence.

The indices must satisfy 0 <= low <= high <= max <= cap(a). The upper capacity bound is checked at run time just like an ordinary slice bound. I find it useful to print both dimensions when debugging ownership:

fmt.Printf("len=%d cap=%d\n", len(prefix), cap(prefix))

Reslicing later cannot recover the fenced-off capacity. The slice header handed to the helper records only a pointer, length, and reduced capacity; it carries no secret reference to my original wider view.

For an array rather than a slice, the omitted low index still defaults to zero. I write all three values at boundaries anyway; explicit fences are easier to review than clever shorthand.

My caveat is that I would not scatter three-index slices everywhere. Most slices should share capacity normally. I use the extra index at ownership boundaries, where an append by a callee would otherwise mutate storage it does not conceptually own.