1

By using the full slice notation b = a[low:high:max] it is possible to get a slice with a smaller capacity than the assigned slice a. The capacity of the slice b will be max - low and the elements storage will be shared with a (no copy is made). max - low must be smaller or equal to cap(a), otherwise we get a panic:

a := make([]byte, 8)
b := a[:3:10]

panic: runtime error: slice bounds out of range [::10] with capacity 8

What is the usefulness of the possibility to get a slice with smaller capacity ?

The only useful application I found is to force a copy of the slice with an append that bypass the capacity of the slice with reduced capacity.

    a := []int{1, 2, 3, 4, 5}
    b := a[1:3:3]
    fmt.Println("a:", a, "b:",b, "cap(b):", cap(b)) // -> a: [1 2 3 4 5] b: [2 3] cap(b): 2
    b[0] = 6
    fmt.Println("a:", a, "b:",b, "cap(b):", cap(b)) // -> a: [1 6 3 4 5] b: [6 3] cap(b): 2
    b = append(b, 7) // copy slice elements and extends capacity of b
    fmt.Println("a:", a, "b:",b, "cap(b):", cap(b)) // -> a: [1 6 3 4 5] b: [6 3 7] cap(b): 4
    b[1] = 8
    fmt.Println("a:", a, "b:",b, "cap(b):", cap(b)) // -> a: [1 6 3 4 5] b: [6 8 7] cap(b): 4

Note that such copy is not obvious and require a comment to make it visible to the reader.

Are the other useful application ?

chmike
  • 20,922
  • 21
  • 83
  • 106

1 Answers1

4

It does force a copy on append, but what it does is slightly more general than that. If you use it, you can pass a slice to some unknown piece of code, with the knowledge that that code won't be able to use the slice to read or write more of the underlying array than you wanted it to.

A slice created through a normal slice expression will have a capacity sufficient to reach the end of the array or slice that it was sliced from, so the caller can re-slice it up to its capacity to gain access to those items. A full slice expression makes this impossible (or impossible beyond a certain point).

This isn't a security measure. Other code in your program can certainly figure out how to read or write past the end of a slice using unsafe, one way or another. It's just a way to prevent unexpected side-effects. If you're calling a function with a slice into the middle of some data that you care about, and that function may append to or otherwise extend the slice, using a full slice expression to limit the capacity is a sensible thing to do.

hobbs
  • 223,387
  • 19
  • 210
  • 288
  • It's also useful to the runtime system itself. You'll see this bleed into your own non-runtime-system code when you use cgo to access a C "array" by C pointer, for instance. These cross-language functions sit right at the edge of "user" code vs "system runtime" code. – torek Jan 10 '20 at 22:51