I'm learning the Go and now I've reached the point of studying arrays and slices. I'm referring to Effective Go and A Tour of Go in this question. I have some doubts in the formulation of slice's capacity. See comments in the code below.
package main
import "fmt"
func main() {
// let's say I need to implement 5x5 picture with 2D slice
// and I have two ways to do that in acc. with
// https://go.dev/doc/effective_go#two_dimensional_slices
// that said:
// 1. six slices - six underlying arrays
picture := make([][]uint8, 5)
for i := range picture {
picture[i] = make([]uint8, 5)
}
// 2. six slices - two* underlying arrays
// *I THOUGHT the efficiency is hiding in number of arrays
picture = make([][]uint8, 5)
pixels := make([]uint8, 25)
for i := range picture {
// every iteration pixels[5:] allocates new array...
picture[i], pixels = pixels[:5], pixels[5:]
// ...but in the new iteration it's deleted
// leaving slice of initial pixels array in picture[i]...
}
// ...and here we have only two arrays...
// ... ^^^ B5t ^^^
// there are six arrays anyway
// but their capacity is 5 for picture and
// 25, 20...5 respectively for its contents
fmt.Print(cap(picture), " ")
for i := range picture {
fmt.Print(cap(picture[i]), " ")
}
fmt.Println()
// if you don't understand let me explain my position
// capacity is characteristic of underlying array rather than
// of slice itself
// in acc. with https://go.dev/tour/moretypes/11
// "The capacity of a slice is the number of
// elements in the underlying array..."
// I believe that capacity of array is immutable one and
// one array cannot have different cap's at the same time
picture = make([][]uint8, 5)
for i := range picture {
picture[i] = make([]uint8, 5)
}
// here we have six arrays but capacity of each is 5
// so the first solution works efficiently than second one
// I believe
fmt.Print(cap(picture), " ")
for i := range picture {
fmt.Print(cap(picture[i]), " ")
}
fmt.Println()
}
The output of this code is:
5 25 20 15 10 5
5 5 5 5 5 5
Can someone explain me where the efficiency of using one allocation method is hiding?
One month later I read this question and A Tour of Go again. Capacity of slice is not a number of elements in underlying array, but it is a number of elements in underlying array counting from the first element in the slice. So the "One allocation method" really allocates only two arrays, not six.
But, with full slice expression, as noticed @rocka2q, one can limit slice along the right border too, so one can't expand it back to it's previous length:
package main
import (
"fmt"
)
func main() {
XSize, YSize := 5, 5
picture := make([][]uint8, YSize)
pixels := make([]uint8, XSize*YSize)
for i := range picture {
// there is no full slice expression here
picture[i], pixels = pixels[:XSize], pixels[XSize:]
}
for i := range picture {
fmt.Print(cap(picture[i]), " ")
}
fmt.Println()
fmt.Println(picture[1][0]) // output: 0
// before adding full slice expressions to the language specification
// there was an opportunity to expand slice back along the right border
// and change the element of another slice through it
fmt.Println(len(picture[0]))
picture[0] = picture[0][:cap(picture[0])]
fmt.Println(len(picture[0]))
picture[0][5] = 1
fmt.Println(picture[1][0]) // output: now it's 1, security hole detected
// with full slice expression one can limit slice capacity
// so there will be no opportunity to expand it back
fmt.Println()
picture = make([][]uint8, YSize)
pixels = make([]uint8, XSize*YSize)
for i := range picture {
// there is full slice expression here
picture[i], pixels = pixels[:XSize:XSize], pixels[XSize:]
}
for i := range picture {
fmt.Print(cap(picture[i]), " ")
}
fmt.Println()
fmt.Println(len(picture[0]))
picture[0] = picture[0][:cap(picture[0])] // cap is 5 now, not 25
fmt.Println(len(picture[0])) // so here len is 5 too, not 25
// picture[0][5] = 1 // panic: out of range [5] with length 5
// so one should use full slice expressions for security purposes
// though there is no advantages on number of allocations
}
Output of this code is:
25 20 15 10 5
0
5
25
1
5 5 5 5 5
5
5