Declare slice or make slice? - go

In Go, what is the difference between var s []int and s := make([]int, 0)?
I find that both works, but which one is better?

Simple declaration
var s []int
does not allocate memory and s points to nil, while
s := make([]int, 0)
allocates memory and s points to memory to a slice with 0 elements.
Usually, the first one is more idiomatic if you don't know the exact size of your use case.

In addition to fabriziom's answer, you can see more examples at "Go Slices: usage and internals", where a use for []int is mentioned:
Since the zero value of a slice (nil) acts like a zero-length slice, you can declare a slice variable and then append to it in a loop:
// Filter returns a new slice holding only
// the elements of s that satisfy f()
func Filter(s []int, fn func(int) bool) []int {
var p []int // == nil
for _, v := range s {
if fn(v) {
p = append(p, v)
}
}
return p
}
It means that, to append to a slice, you don't have to allocate memory first: the nil slice p int[] is enough as a slice to add to.

Just found a difference. If you use
var list []MyObjects
and then you encode the output as JSON, you get null.
list := make([]MyObjects, 0)
results in [] as expected.

A bit more complete example (one more argument in .make()):
slice := make([]int, 2, 5)
fmt.Printf("length: %d - capacity %d - content: %d", len(slice), cap(slice), slice)
Out:
length: 2 - capacity 5 - content: [0 0]
Or with a dynamic type of slice:
slice := make([]interface{}, 2, 5)
fmt.Printf("length: %d - capacity %d - content: %d", len(slice), cap(slice), slice)
Out:
length: 2 - capacity 5 - content: [<nil> <nil>]

Related

Slice out of bounds using [][]int but works with map[int][]int

Why does this code work
graph := make(map[int][]int, 0)
graph[0] = append(graph[0], 1)
But if you replace first line with graph := make([][]int, 0) I get panic: runtime error: index out of range? It's very weird.
Lets simplify your code, to make it more obvious what's happening (Playground link):
graph1 := make(map[int]int, 0)
graph2 := make([]int, 0)
x := graph1[0] // Success
y := graph2[0] // Panic
From this we see that the difference is due to map[int] vs []int -- the second array in your type is actually irrelevant.
Now to understand why this is happening, we need to understand how Go handles map and slice access. From Go Maps in Action we learn:
If the requested key doesn't exist, we get the value type's zero value.
In your original code, the zero value for a slice ([]int), is nil, and append() treats nil as the first argument as an empty slice.
But when we try to access the 0th element of an empty slice, we get a panic, because the slice is empty.
In summary, append and the second slice of your type are both red herrings in your question. The panic happens when trying to access the non-existent element in the first dimension of your slice.
When you do make in graph := make(map[int][]int, 0), you are allocating memory to your map, not to array. So you might do this only
graph := make(map[int][]int).
Decomping you code:
type a []int
type m map[int]a
func main() {
fmt.Println("Hello, playground")
//decomping graph := make(map[int][]int, 0)
graph := make(m)
//map is empty
fmt.Println(len(graph))
//decomping graph[0] := append(graph[0], 1)
itm_a := 1
arr_a := []int{}
//appeding item to "a" type
arr_a = append(arr_a, itm_a)
//appending array of a to graph
graph[0] = arr_a
//print graph
fmt.Println(graph)
}
See in playground.
The error that you are getting is caused for conceptual error.
When you does graph := make([][]int, 0), you are allocation memory to a slice of slice, not an array. See in https://blog.golang.org/go-slices-usage-and-internals.
So you can does this (decomping solution):
type a []int
type m []a
func main() {
fmt.Println("Hello, playground")
//decomping graph := make([][]int, 0)
//see that you must be set the length
graph := make(m, 0)
//map is empty
fmt.Println(len(graph))
//this is incorrect: graph[0] := append(graph[0], 1)
//this is correct: graph[0] := append(graph[0], []int{1})
//see:
itm_a := 1
arr_a := []int{}
//appeding item to "a" type
arr_a = append(arr_a, itm_a)
//appending slice of a to graph (slice)
graph = append(graph, arr_a)
//print graph
fmt.Println(graph)
}
See in playground
The code panics due to slice length being 0. If you want to append anything to the slice, you just have to provide its length as below.
graph := make([][]int, 1)
fmt.Println(len(graph))
graph[0] = append(graph[0], 1)
fmt.Println(graph)
To append data to a slice at first level, append to its first index and then append to second level just like below.
graph := make([][]int, 0)
fmt.Println(len(graph))
graph = append(graph, []int{1})
Check Playground example
make(map[int][]int, 0) creates a map of []int.
By design in Go you can get any element from a map. And if it doesn't exist you receive the "zero" value which here is an empty slice.
graph := make(map[int][]int)
graph[4] = append(graph[4], 1)
graph[7] = append([]int{}, 1, 2)
graph[11] = append([]int{1, 2, 3}, 4, 5)
printing it gives this slice:
fmt.Printf("%#v\n", graph)
map[int][]int{
4:[]int{1},
7:[]int{1, 2},
11:[]int{1, 2, 3, 4, 5},
}
Your second example create an empty slice of []int slices. Slices work differently from maps, so indexing an element that doesn't exist will give you a panic.

unexpected slice append behaviour

I encountered weird behaviour in go code today: when I append elements to slice in loop and then try to create new slices based on the result of the loop, last append overrides slices from previous appends.
In this particular example it means that sliceFromLoop j,g and h slice's last element are not 100,101 and 102 respectively, but...always 102!
Second example - sliceFromLiteral behaves as expected.
package main
import "fmt"
func create(iterations int) []int {
a := make([]int, 0)
for i := 0; i < iterations; i++ {
a = append(a, i)
}
return a
}
func main() {
sliceFromLoop()
sliceFromLiteral()
}
func sliceFromLoop() {
fmt.Printf("** NOT working as expected: **\n\n")
i := create(11)
fmt.Println("initial slice: ", i)
j := append(i, 100)
g := append(i, 101)
h := append(i, 102)
fmt.Printf("i: %v\nj: %v\ng: %v\nh:%v\n", i, j, g, h)
}
func sliceFromLiteral() {
fmt.Printf("\n\n** working as expected: **\n")
i := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
fmt.Println("initial slice: ", i)
j := append(i, 100)
g := append(i, 101)
h := append(i, 102)
fmt.Printf("i: %v\nj: %v\ng: %v\nh:%v\n", i, j, g, h)
}
link to play.golang:
https://play.golang.org/p/INADVS3Ats
After some reading, digging and experimenting I found that this problem is originated in slices referencing the same underlaying array values and can be solved by copying slice to new one before appending anything, however it looks quite... hesitantly.
What's the idomatic way for creating many new slices based on old ones and not worrying about changing values of old slices?
Don't assign append to anything other than itself.
As you mention in the question, the confusion is due to the fact that append both changes the underlying array and returns a new slice (since the length might be changed). You'd imagine that it copies that backing array, but it doesn't, it just allocates a new slice object that points at it. Since i never changes, all those appends keep changing the value of backingArray[12] to a different number.
Contrast this to appending to an array, which allocates a new literal array every time.
So yes, you need to copy the slice before you can work on it.
func makeFromSlice(sl []int) []int {
result := make([]int, len(sl))
copy(result, sl)
return result
}
func main() {
i := make([]int, 0)
for ii:=0; ii<11; ii++ {
i = append(i, ii)
}
j := append(makeFromSlice(i), 100) // works fine
}
The slice literal behavior is explained because a new array is allocated if the append would exceed the cap of the backing array. This has nothing to do with slice literals and everything to do with the internals of how exceeding the cap works.
a := []int{1,2,3,4,5,6,7}
fmt.Printf("len(a) %d, cap(a) %d\n", a, len(a), cap(a))
// len(a) 7, cap(a) 7
b := make([]int, 0)
for i:=1; i<8, i++ {
b = append(b, i)
} // b := []int{1,2,3,4,5,6,7}
// len(b) 7, cap(b) 8
b = append(b, 1) // any number, just so it hits cap
i := append(b, 100)
j := append(b, 101)
k := append(b, 102) // these work as expected now
If you need a copy of a slice, there's no other way to do it other than, copying the slice. You should almost never assign the result of append to a variable other than the first argument of append. It leads to hard to find bugs, and will behave differently depending on whether the slice has the required capacity or not.
This isn't a commonly needed pattern, but as with all things of this nature if you need to repeate a few lines of code multiple times, then you can use a small helper function:
func copyAndAppend(i []int, vals ...int) []int {
j := make([]int, len(i), len(i)+len(vals))
copy(j, i)
return append(j, vals...)
}
https://play.golang.org/p/J99_xEbaWo
There is also a little bit simpler way to implement copyAndAppend function:
func copyAndAppend(source []string, items ...string) []string {
l := len(source)
return append(source[:l:l], items...)
}
Here we just make sure that source has no available capacity and so copying is forced.

Golang: Problems when using append on slice

I am using golang. Here is my code:
func main() {
quanPailie([]int{1, 2})
}
func quanPailie(nums []int) [][]int {
COUNT := len(nums)
//only one item
if COUNT == 1 {
return [][]int{nums}
}
insertItem(quanPailie(nums[:COUNT-1]), nums[COUNT-1])
return [][]int{}
}
func insertItem(res [][]int, insertNum int) {
fmt.Println("insertItem,res:", res, "insertNum", insertNum) //insertItem,res: [[1]] insertNum 2
for _, v := range res {
for i := 0; i < len(v); i++ {
fmt.Println("===before,v:", v)
c := append(v[:i], append([]int{insertNum}, v[i:]...)...)
fmt.Println("===after,v:", v)
fmt.Println("ccc", c)
}
}
}
What makes me very confused is the output:
===before,v: [1]
===after,v: [2]
Why did the value of v change? Hope someone can help me. Thanks a lot.
Go playground: https://play.golang.org/p/wITYsGpX7U
EDIT:
Thanks for icza's great help, I think I have understood this problem.
And, here is a simple code to show this issue.
func test1() {
nums := []int{1, 2, 3}
_ = append(nums[:2], 4)
fmt.Println("test1:", nums)
//nums changes because the cap is big enought, the original array is modified.
}
func test2() {
nums := []int{1, 2, 3}
c := append(nums[:2], []int{4, 5, 6}...)
fmt.Println("test2:", nums)
fmt.Println("cc:", c)
//nums dont't change because the cap isn't big enought.
//a new array is allocated while the nums still points to the old array.
//Of course, the return value of append points to the new array.
}
Go playground: https://play.golang.org/p/jBNFsCqUn3
This is the code in question:
fmt.Println("===before,v:", v)
c := append(v[:i], append([]int{insertNum}, v[i:]...)...)
fmt.Println("===after,v:", v)
You ask why v changes between the 2 Println() statements.
Because you are using the builtin append() function, quoting from its doc:
The append built-in function appends elements to the end of a slice. If it has sufficient capacity, the destination is resliced to accommodate the new elements. If it does not, a new underlying array will be allocated. Append returns the updated slice.
So if the slice you append to has enough room (capacity) to accomodate the elements you want to append, no new slice will be allocated, instead the destination slice will be re-sliced (which will use the same underlying array) and append will happen in that.
Let's check the capacity:
fmt.Println("===before,v:", v, cap(v))
c := append(v[:i], append([]int{insertNum}, v[i:]...)...)
fmt.Println("===after,v:", v, cap(v))
Output:
===before,v: [1] 2
===after,v: [2] 2
The v slice has a capacity of 2. When for loop starts, i=0, v[:i] is v[:0] which is an empty slice (but has capacity 2) and so appending 1 or 2 elements will not allocate a new array/slice, it will be done "in place". This "in place" is the 0th element of v, since v[:i] is shorthand for v[0:i]. Hence the elements will be appended starting from v[0] in the underlying array which is shared, so the element denoted by v[0] will change.
Note that slicing a slice results in a slice which shares its underlying backing array with the original slice (does not make a copy of the elements).
If you want to avoid this, use or allocate a new slice, copy original content and append to the new slice, e.g.:
src := []int{1, 2}
c := make([]int, len(src))
copy(c, src)
// Append something:
c = append(c, 3, 4)
fmt.Println(src) // [1 2] - src doesn't change
fmt.Println(c) // [1 2 3 4]

The zero value of a slice is not nil

I was following the example https://tour.golang.org/moretypes/10
I modified the code expecting to get the same result. I did not. Is this a bug, or a documentation error? The tour states
A nil slice has a length and capacity of 0.
My y variable has a length and capacity of 0.
package main
import "fmt"
func myPrint(z []int) {
fmt.Println(z, len(z), cap(z))
if z == nil {
fmt.Println("nil!")
}
}
func main() {
var z []int
y := []int {}
myPrint(z)
myPrint(y)
}
Here is my output.
[] 0 0
nil!
[] 0 0
I was expecting a second "nil"~ Why didn't I get it?
nil Vs empty slice
If we think of a slice like this:
[pointer] [length] [capacity]
then:
nil slice: [nil][0][0]
empty slice: [addr][0][0] // it points to an address
From: "Go in action" book:
nil slice
They’re useful when you want to represent a slice that doesn’t exist, such as when an exception occurs in a function that returns a slice.
// Create a nil slice of integers.
var slice []int
empty slice
Empty slices are useful when you want to represent an empty collection, such as when a database query returns zero results.
// Use make to create an empty slice of integers.
slice := make([]int, 0)
// Use a slice literal to create an empty slice of integers.
slice := []int{}
👉 Regardless of whether you’re using a nil slice or an empty slice, the built-in functions append, len, and cap work the same.
Go playground example:
package main
import (
"fmt"
)
func main() {
var nil_slice []int
var empty_slice = []int{}
fmt.Println(nil_slice == nil, len(nil_slice), cap(nil_slice))
fmt.Println(empty_slice == nil, len(empty_slice), cap(empty_slice))
}
prints:
true 0 0
false 0 0
The doc you referenced states that a nil slice has a length and capacity of 0, but not that every slice of length and capacity of zero is a nil slice. The specification only says that the value of an uninitialized slice is nil.
This is a convenience to support len and cap on slices which are uninitialised (nil). Otherwise we would need to check for non-nil first in order to avoid panic. (This also holds for other in-built types like maps or channels.)
In terms of the fmt.Print output, the difference in behaviour is similar to printing an uninitialised (nil) pointer vs pointer to an empty structure:
var s *struct{} // uninitialised pointer
fmt.Println(s) // <nil>
s = &struct{}{} // pointer to an empty structure
fmt.Println(s) // &{}
In this case:
var z []int
You have declared a variable z but you did not initialize it.
In this case:
y := []int {}
You declared it and initialized it, you set it to an empty slice. Writing the second expression the long way makes the difference between the two expressions more clear:
var y []int = []int {}
Your y variable isn't the zero value for a slice. It's allocated via an empty slice literal.
// both of these allocate a slice
y := []int{}
z := []int{1, 2, 3}
A nil slice has a length and capacity of 0 and has no underlying array.
var s []string => no underlying array
var s = []string => create a underlying array but his length is 0.

How to preallocate and fill a slice of pointers in a go-idiomatic way?

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?
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.
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.
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.
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.

Resources