Reputation: 11264
http://play.golang.org/p/j-Y0mQzTdP
package main
import "fmt"
type UselessStruct struct {
a int
b int
}
func main() {
mySlice := make([]*UselessStruct, 5)
for i := 0; i != 5; i++ {
mySlice = append(mySlice, &UselessStruct{})
}
fmt.Println(mySlice)
}
Outputs: [<nil> <nil> <nil> <nil> <nil> 0xc010035160 0xc010035170 0xc010035180 0xc010035190 0xc0100351a0]
What i would like to do, is preallocate memory for 5 UselessStructs, stored as pointers. If i declare a slice of struct values eq:
mySlice := make([]UselessStruct, 5)
then this creates 5 empty structs - appending doesn't replace the empty structs, but rather keeps on adding to the slice, so the end result with this code:
http://play.golang.org/p/zBYqGVO85h
package main
import "fmt"
type UselessStruct struct {
a int
b int
}
func main() {
mySlice := make([]UselessStruct, 5)
for i := 0; i != 5; i++ {
mySlice = append(mySlice, UselessStruct{})
}
fmt.Println(mySlice)
}
is: [{0 0} {0 0} {0 0} {0 0} {0 0} {0 0} {0 0} {0 0} {0 0} {0 0}]
What is the the go-idiomatic way to preallocate and fill slices?
Upvotes: 41
Views: 47945
Reputation: 25237
Are you sure you need pointers? Your struct has a zero value so:
mySlice := make([]UselessStruct, 5) // has memory preallocated for 5 UselessStructs.
And since slices are reference types you effectively have 5 pointers to those 5 UselessStructs.
If you need to get a reference to an individual struct to pass around then you can just so
myStruct := &mySlice[0]
And now you have a pointer to a UselessStruct
to use as you see fit. It's far less code than you have and leverages Go's zero value feature.
Upvotes: 4
Reputation: 53418
For your first example, I would do:
mySlice := make([]*UselessStruct, 5)
for i := range mySlice {
mySlice[i] = new(UselessStruct)
}
The issue you are facing in both examples is you are appending to a slice that is already the correct length. If you set mySlice := make([]*UselessStruct, 5)
, you are asking for a slice of nil pointers of length 5. If you append one pointer, it now has length 6.
Instead, you want to use mySlice := make([]*UselessStruct, 0, 5)
. This creates a slice of length 0 but capacity 5. Each time you append it will add one to the length but it won't reallocate until you exceed the slice's capacity.
mySlice := make([]*UselessStruct, 0, 5)
for i := 0; i != 5; i++ {
mySlice = append(mySlice, &UselessStruct{})
}
// mySlice is [0xc010035160 0xc010035170 0xc010035180 0xc010035190 0xc0100351a0]
Both of my examples will work as you expect but I recommend the first one for purely style reasons.
Upvotes: 57
Reputation: 2141
Just to complete: append works with nil slice so, you don't need to create the slice with make, you can just append element to it.
var mySlice []*UselessStruct
for i := 0; i < 5; i++ {
mySlice = append(mySlice, &UselessStruct{})
}
This will do the same as previous example without pre-allocation, but if you know the size, you rather use something like this one:
mySlice := make([]*UselessStruct, 0, 5)
for i := range mySlice {
mySlice[i] = &UselessStruct{}
}
This may avoid some reallocation.
Upvotes: 1
Reputation: 26410
There are two ways to do this. One is by pre-allocating the slots as you did.
But instead of using append
, you simply index into one of the existing slots:
mySlice[i] = &UselessStruct{}
The second is to use the 'overloaded' verion of make
. You specify zero length, but a capacity of 5.
package main
type T struct {
A int
B int
}
func main() {
mySlice := make([]*T, 0, 5)
for i := 0; i < 5; i++ {
mySlice = append(mySlice, &T{1, 2})
}
}
mySlice := make([]*T, 0, 5)
initializes the slice with a length of zero, but it still pre-allocates enough space for 5 entries.
Upvotes: 9