0

Note: Declare slice or make slice? does not answer my question because that compares slice declaration vs. make slice whereas my question compares slice literal vs. make slice. That question has an easy answer because a bare slice declaration creates a nil slice, however, if you read my question below carefully, I do not create a nil slice at all.

There are two ways to create a slice and append to it. My code below shows both ways as Example 1 and Example 2.

package main

import (
    "fmt"
)

func main() {
    // Example 1
    a := []int{}
    fmt.Printf("len(a): %d; cap(a): %d; a: %v\n", len(a), cap(a), a)
    a = append(a, 10, 20, 30, 40, 50)
    fmt.Printf("len(a): %d; cap(a): %d; a: %v\n", len(a), cap(a), a)
    
    // Example 2
    b := make([]int, 0)
    fmt.Printf("len(b): %d; cap(b): %d; b: %v\n", len(b), cap(b), b)
    b = append(b, 10, 20, 30, 40, 50)
    fmt.Printf("len(b): %d; cap(b): %d; b: %v\n", len(b), cap(b), b)
}

Output:

len(a): 0; cap(a): 0; a: []
len(a): 5; cap(a): 6; a: [10 20 30 40 50]
len(b): 0; cap(b): 0; b: []
len(b): 5; cap(b): 6; b: [10 20 30 40 50]

Are both ways of creating empty slice with []int{} and make([]int, 0) equivalent? Is there any situation where they make a difference in behavior?

Lone Learner
  • 18,088
  • 20
  • 102
  • 200
  • The linked question in turn links to the relevant documentation: https://blog.golang.org/slices-intro. TL;DR: there is no functional difference between `x := []int{}` and `x := make([]int, 0)`. – Adrian Aug 19 '20 at 18:38
  • You are correct. I misspoke. `x := []int{}` is identical to `make([]int, 0)`. Still, the distinction you claim does not exist. – Jonathan Hall Aug 19 '20 at 19:21
  • @Flimzy The fact that `x := []int{}` is identical to `make([]int, 0)` would make a valid answer to this question. This fact is not discussed in the linked question. Wouldn't it be nice to post an answer to this question explaining that both are identical? – Lone Learner Aug 19 '20 at 19:24
  • @Volker Take a look at the Go spec here: golang.org/ref/spec . If you export it as PDF, you get 84 pages. It has over 25,000 words. I don't know about you but I sure cannot read a 25,000-word technical document in an hour! – Lone Learner Aug 21 '20 at 15:02
  • Here is a good answer on this topic: https://stackoverflow.com/questions/29164375/correct-way-to-initialize-empty-slice/29164565#29164565 – Lone Learner Aug 21 '20 at 15:02
  • Possible duplicate of [nil slices vs non-nil slices vs empty slices in Go language](https://stackoverflow.com/questions/44305170/nil-slices-vs-non-nil-slices-vs-empty-slices-in-go-language/44305910#44305910). – icza Aug 22 '20 at 21:18

1 Answers1

4

I modified your example a bit

    // Example 1
    a := []int{}
    pa := &a
  
    // Example 2
    b := make([]int, 0)
    pb := &b

    runtime.KeepAlive(pa)
    runtime.KeepAlive(pb)

It is compiled to:

*** main.go#12   >    a := []int{}
0x4e56a9    488d0538bb1100          lea rax, ptr [runtime.zerobase]
0x4e56b0    4889442470          mov qword ptr [rsp+0x70], rax
0x4e56b5    8400                test byte ptr [rax], al
0x4e56b7    eb00                jmp 0x4e56b9
0x4e56b9    4889842418010000        mov qword ptr [rsp+0x118], rax
0x4e56c1    0f57c0              xorps xmm0, xmm0
0x4e56c4    0f11842420010000        movups xmmword ptr [rsp+0x120], xmm0
*** main.go#13   >    pa := &a
0x4e56cc    488d842418010000        lea rax, ptr [rsp+0x118]
0x4e56d4    4889442460          mov qword ptr [rsp+0x60], rax
*** main.go#16   >    b := make([]int, 0)
0x4e56d9    488d0520020100          lea rax, ptr [__image_base__+1005824]
0x4e56e0    48890424            mov qword ptr [rsp], rax
0x4e56e4    0f57c0              xorps xmm0, xmm0
0x4e56e7    0f11442408          movups xmmword ptr [rsp+0x8], xmm0
0x4e56ec    e8bf49f6ff          call $runtime.makeslice
0x4e56f1    488b442418          mov rax, qword ptr [rsp+0x18]
0x4e56f6    4889842400010000        mov qword ptr [rsp+0x100], rax
0x4e56fe    0f57c0              xorps xmm0, xmm0
0x4e5701    0f11842408010000        movups xmmword ptr [rsp+0x108], xmm0
*** main.go#17   >    pb := &b
0x4e5709    488d842400010000        lea rax, ptr [rsp+0x100]
0x4e5711    4889442458          mov qword ptr [rsp+0x58], rax

Might seem that make([]int, 0) causes heap allocation (through $runtime.makeslice()) but no - diving into sources shows that makeslice() also returns &zerobase-based slice:

    if size == 0 {
        return unsafe.Pointer(&zerobase)
    }

Thus both snippets give same slice structure where data pointer is set to zerobase.

maxim_ge
  • 1,153
  • 1
  • 10
  • 18
  • 3
    They are semantically equivalent. The fact that escape analysis differs in your example is not guaranteed to be consistent, and has no basis in the language specification. – JimB Aug 19 '20 at 19:34
  • 1
    @JimB Figured out that cases are equivalent in allocation terms, I editied the answer. – maxim_ge Aug 19 '20 at 19:49
  • 1
    @maxim_ge Thanks for the detailed answer. This answers my question very well. May I suggest that you edit the answer to remove the incorrect conclusion you drew earlier and write only the correct conclusion. That would make your answer clearer and easier to read. – Lone Learner Aug 20 '20 at 05:40