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?
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?
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.
var p []int
easier than using make
(which I associate more with allocation, even though with a 0 cap, it would not allocate anything). In term of readability, I prefer not using make
here. –
Cleaning p := []int{}
). Since we usually use :=
syntax to declare most variables, it's more natural to have it everywhere instead of having an exceptions for slices. Other than this trying to think of allocations usually push people toward premature optimizations. –
Derinna 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.
make
maps, because even an empty map
needs space allocated for some bookkeeping. –
Mitten make(..)
except that there is no option to specify the initial size. play.golang.org/p/gn1kVAfZcS –
Alphaalphabet nil
in case your slice doesn't have any element, rather than an empty array. However, if make
is used to create the slice, an empty array will be returned instead, which is generally the desired effect. –
Coricoriaceous var s []int
) will produce null
, while marshaling the empty slice (s := make([]int, 0)
) will produce the expected []
–
Dilworth make
is not applied automatically? We always need memory and point to it to use the variable. So why is make
part of the language at all? –
Portend s := []float64{5, 6, 7}
, I use it a lot yet doesn't quite understand it. –
Laryngitis make
won't allocate if len is zero, there is no concept of allocating zero bytes. Pointers to zero sized things will have a shared specific address in Go. –
Derinna 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.
var p []int
easier than using make
(which I associate more with allocation, even though with a 0 cap, it would not allocate anything). In term of readability, I prefer not using make
here. –
Cleaning p := []int{}
). Since we usually use :=
syntax to declare most variables, it's more natural to have it everywhere instead of having an exceptions for slices. Other than this trying to think of allocations usually push people toward premature optimizations. –
Derinna 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>]
Slice can grow beyond its capacity and this means memory re-allocation which may be costly (it takes some time). There is a rule on how slices will grow:
Go slices grow by doubling until size 1024, after which they grow by 25% each time
Now let's compare two approaches:
var p []int
p := make([]int, 0, len(s))
Below we have two functions, one with only declaration of the slice and other with slice created using make
and with its capacity set.
func FilterSliceNil(s []int, fn func(int) bool) []int {
var p []int // == nil
for _, v := range s {
if fn(v) {
p = append(p, v)
}
}
return p
}
func FilterSliceCapSet(s []int, fn func(int) bool) []int {
p := make([]int, 0, len(s)) // Set length=0 and capacity to length of s
for _, v := range s {
if fn(v) {
p = append(p, v)
}
}
return p
}
Benchmark test functions:
package main
import (
"testing"
)
var slice = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
var f = func(i int) bool {
return i%2 == 0
}
func Benchmark_FilterSliceNil(b *testing.B) {
for i := 0; i < b.N; i++ {
FilterSliceNil(slice, f)
}
}
func Benchmark_FilterSliceCapSet(b *testing.B) {
for i := 0; i < b.N; i++ {
FilterSliceCapSet(slice, f)
}
}
Benchmark results:
$ go test -bench=. -benchtime=10s
goos: linux
goarch: amd64
pkg: example.com/Shadowing
cpu: 13th Gen Intel(R) Core(TM) i7-13700K
Benchmark_FilterSliceNil-24 157664690 76.96 ns/op
Benchmark_FilterSliceCapSet-24 330690351 36.27 ns/op
PASS
ok example.com/Filter 35.479s
On my machine the version with make
takes half the time to complete.
© 2022 - 2025 — McMap. All rights reserved.
nil
slice, whereas the second creates anempty
slice (this is the terminology used by the "Go in action book"). To avoid posting the same answer here too, you can check https://mcmap.net/q/86042/-correct-way-to-initialize-empty-slice – Cheese