Quoting from Spec: Slice types:
A slice is a descriptor for a contiguous segment of an underlying array and provides access to a numbered sequence of elements from that array.
A slice always denotes a contiguous segment of an underlying array, elements cannot be skipped between the start and end indices.
Using a slice of pointers
You could mimic the behavior with pointers, but then they will be pointers with all their pros and cons. For example:
const N = 3
s := make([][N]int, 10)
s[2] = [3]int{20, 21, 22}
s[5] = [3]int{50, 51, 52}
s[9] = [3]int{90, 91, 92}
s2 := []*[N]int{&s[2], &s[5], &s[9]}
for _, p := range s2 {
fmt.Print(*p, " ")
}
s[2][0] = 66
fmt.Println()
for _, p := range s2 {
fmt.Print(*p, " ")
}
After creating and populating s2
(from elements of s
), we modify elements of s
, and print s2
again, and we see the changed, updated value. Output (try it on the Go Playground):
[20 21 22] [50 51 52] [90 91 92]
[66 21 22] [50 51 52] [90 91 92]
Using a slice of slices
Slices (slice headers) are similar to pointers: they are small, struct like data structures which contain a pointer to the first element of the slice, pointing to somewhere in an underlying array. For details, see Are golang slices passed by value? and why slice values can sometimes go stale but never map values?
So if you would use a slice of slices instead of a slice of arrays, it would work without pointers (slice values already contain pointers).
See this example:
s := make([][]int, 10)
s[2] = []int{20, 21, 22}
s[5] = []int{50, 51, 52}
s[9] = []int{90, 91, 92}
s2 := [][]int{s[2], s[5], s[9]}
fmt.Println(s2)
s[2][0] = 66
fmt.Println(s2)
This will output (try it on the Go Playground):
[[20 21 22] [50 51 52] [90 91 92]]
[[66 21 22] [50 51 52] [90 91 92]]
See related question: How is two dimensional array's memory representation