Search code examples
stringgodynamic-memory-allocation

Are these 2 string allocations equivalent?


I saw this guy, and he wrote this code:

func foo1() *string {
    var pointa *string
    pointa = new(string)
    *pointa = "stuff"
    return pointa
}

Very nice, very nice, however, can't help but notice that one could also write:

func foo2() *string {
    var pointa *string
    var str = "stuff"
    pointa = &str
    return pointa
}

The first code says:

  • dynamically allocate a string container with empty underlying storage
  • override the underlying storage with contents of this literal
  • I assume, because of COW, underlying storage just points to the literal's data in the data segment of ELF

The second code says:

  • allocate locally an instance of a string
  • but because it will later escape the scope via reference &, it will be allocated dynamically
  • and still, because of COW, no copy will take place

Am I right about these assumptions? Are these 2 dynamic string allocations equivalent? Is new(string); *pointer = "payload" exactly what is happening in the second code, but under the hood, implicitly?

Are these pieces of code gonna produce the same code that allocated and initializes the string?


Solution

  • The Go specification makes no guarantees, and whatever answer you discover today is subject to change without warning, but you can see for yourself using the version of gc available a godbolt.org that all three versions of your function produce identical output, differing only in the names and values of the string (stuff1/stuff2/stuff3):

    https://godbolt.org/z/evP1M3fs5

    func foo1() *string {
        var pointa *string
        pointa = new(string)
        *pointa = "stuff1"
        return pointa
    }
    
    func foo2() *string {
        var pointa *string
        var str = "stuff2"
        pointa = &str
        return pointa
    }
    
    func foo3() *string {
        x := "stuff3"
        return &x;
    }
    

    Output:

    main_foo1_pc0:
            TEXT    main.foo1(SB), ABIInternal, $24-0
            CMPQ    SP, 16(R14)
            PCDATA  $0, $-2
            JLS     main_foo1_pc50
            PCDATA  $0, $-1
            PUSHQ   BP
            MOVQ    SP, BP
            SUBQ    $16, SP
            FUNCDATA        $0, gclocals·g2BeySu+wFnoycgXfElmcg==(SB)
            FUNCDATA        $1, gclocals·g2BeySu+wFnoycgXfElmcg==(SB)
            LEAQ    type:string(SB), AX
            PCDATA  $1, $0
            CALL    runtime.newobject(SB)
            MOVQ    $6, 8(AX)
            LEAQ    go:string."stuff1"(SB), CX
            MOVQ    CX, (AX)
            ADDQ    $16, SP
            POPQ    BP
            RET
    main_foo1_pc50:
            NOP
            PCDATA  $1, $-1
            PCDATA  $0, $-2
            CALL    runtime.morestack_noctxt(SB)
            PCDATA  $0, $-1
            JMP     main_foo1_pc0
    main_foo2_pc0:
            TEXT    main.foo2(SB), ABIInternal, $24-0
            CMPQ    SP, 16(R14)
            PCDATA  $0, $-2
            JLS     main_foo2_pc50
            PCDATA  $0, $-1
            PUSHQ   BP
            MOVQ    SP, BP
            SUBQ    $16, SP
            FUNCDATA        $0, gclocals·g2BeySu+wFnoycgXfElmcg==(SB)
            FUNCDATA        $1, gclocals·g2BeySu+wFnoycgXfElmcg==(SB)
            LEAQ    type:string(SB), AX
            PCDATA  $1, $0
            CALL    runtime.newobject(SB)
            MOVQ    $6, 8(AX)
            LEAQ    go:string."stuff2"(SB), CX
            MOVQ    CX, (AX)
            ADDQ    $16, SP
            POPQ    BP
            RET
    main_foo2_pc50:
            NOP
            PCDATA  $1, $-1
            PCDATA  $0, $-2
            CALL    runtime.morestack_noctxt(SB)
            PCDATA  $0, $-1
            JMP     main_foo2_pc0
    main_foo3_pc0:
            TEXT    main.foo3(SB), ABIInternal, $24-0
            CMPQ    SP, 16(R14)
            PCDATA  $0, $-2
            JLS     main_foo3_pc50
            PCDATA  $0, $-1
            PUSHQ   BP
            MOVQ    SP, BP
            SUBQ    $16, SP
            FUNCDATA        $0, gclocals·g2BeySu+wFnoycgXfElmcg==(SB)
            FUNCDATA        $1, gclocals·g2BeySu+wFnoycgXfElmcg==(SB)
            LEAQ    type:string(SB), AX
            PCDATA  $1, $0
            CALL    runtime.newobject(SB)
            MOVQ    $6, 8(AX)
            LEAQ    go:string."stuff3"(SB), CX
            MOVQ    CX, (AX)
            ADDQ    $16, SP
            POPQ    BP
            RET
    main_foo3_pc50:
            NOP
            PCDATA  $1, $-1
            PCDATA  $0, $-2
            CALL    runtime.morestack_noctxt(SB)
            PCDATA  $0, $-1
            JMP     main_foo3_pc0