Var a []int <- does not allocate memory until first usage?

Will it not allocate 2 ints worth of memory: one to store the length of 0, and another one to store a nil pointer to an underlying array ?

Will it allocate these 2 ints on its first usage ?

Also, which one do you prefer:
var a []int VS a := []int{}

this question is the one before last, at the bottom, from here https://www.toptal.com/go/interview-questions

1 Like

The zerovalue for []Type is nil, so no allocation necessary before you actually create a non-zero value.

Also var a []int is not equivalent to a := []int{} as the latter should already initialise with a non-zero value.

2 Likes

The declaration

var a [] int

allocates memory for the runtime/slice.go slice struct

type slice struct {
	array unsafe.Pointer
	len   int
	cap   int
}

The value of the pointer to the underlying array is nil. No memory is allocated for the underlying array.

The Go Programming Language Specification

The zero value

When storage is allocated for a variable, either through a declaration or a call of new , or when a new value is created, either through a composite literal or a call of make , and no explicit initialization is provided, the variable or value is given a default value. Each element of such a variable or value is set to the zero value for its type: false for booleans, 0 for numeric types, "" for strings, and nil for pointers, functions, interfaces, slices, channels, and maps.

From the Go Wiki:

Go Code Review Comments

This page collects common comments made during reviews of Go code, so that a single detailed explanation can be referred to by shorthands. This is a laundry list of common mistakes, not a comprehensive style guide.

You can view this as a supplement to Effective Go.

Declaring Empty Slices

When declaring an empty slice, prefer

var t []string

over

t := []string{}

The former declares a nil slice value, while the latter is non-nil but zero-length. They are functionally equivalent—their len and cap are both zero—but the nil slice is the preferred style.

Note that there are limited circumstances where a non-nil but zero-length slice is preferred, such as when encoding JSON objects (a nil slice encodes to null , while []string{} encodes to the JSON array [] ).

When designing interfaces, avoid making a distinction between a nil slice and a non-nil, zero-length slice, as this can lead to subtle programming errors.

For more discussion about nil in Go see Francesc Campoy’s talk Understanding Nil.

Hi @petrus and @NobbZ

so what gets allocated, at runtime is:
for var a []int :

nil   <-  but  the type of this nil value is "slice of int"

for t := []string{}:

type slice struct {  <- 3 fields, one int for each field
	array nil
	len   0
	cap   0
}

Is this the difference ?

No. This is the difference.

package main

import (
	"fmt"
	"unsafe"
)

type slice struct {
	array unsafe.Pointer
	len   int
	cap   int
}

func main() {
	var a []int
	printSlice(&a)
	fmt.Println()
	b := []int{}
	printSlice(&b)
	fmt.Println()
}

func printSlice(p *[]int) {
	s := *p
	fmt.Printf("%[1]v %[2]p %[1]T %[3]t\n", s, p, s == nil)
	ss := (*slice)(unsafe.Pointer(p))
	fmt.Println(ss.array, ss.len, ss.cap)
}
[] 0xc00000c0a0 []int true
<nil> 0 0

[] 0xc00000c0c0 []int false
0x586a00 0 0

The declarations of a and b allocate zero-value slice structs at 0xc00000c0a0 and 0xc00000c0c0. The assignment of the composite literal []int{} to b allocates an underlying array at 0x586a00.

Hi @petrus

Makes sense, to sum it up:
so what gets allocated, at runtime is:
for var a []int :

type slice struct {  <- 3 fields, one int for each field
	array nil  <- nil pointer
	len   0
	cap   0
}

for t := []string{} :

type slice struct {  <- 3 fields, one int for each field
	array 0x-...  <- an actual pointer
	len   0
	cap   0
}

Memory wise they would occupy the same. One has a nil internal pointer and the other has a non nil internal pointer.

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.