Defining golang struct function using pointer or not

前端 未结 4 1943
青春惊慌失措
青春惊慌失措 2021-01-31 03:12

Can someone explain to me why appending to an array works when you do this:

func (s *Sample) Append(name string) {
    d := &Stuff{
        name: name,
    }         


        
相关标签:
4条回答
  • 2021-01-31 03:35

    As mentioned in the FAQ

    Should I define methods on values or pointers?

    func (s *MyStruct) pointerMethod() { } // method on pointer
    func (s MyStruct)  valueMethod()   { } // method on value
    

    First, and most important, does the method need to modify the receiver? If it does, the receiver must be a pointer. (Slices and maps act as references, so their story is a little more subtle, but for instance to change the length of a slice in a method the receiver must still be a pointer.)

    In the examples above, if pointerMethod modifies the fields of s, the caller will see those changes, but valueMethod is called with a copy of the caller's argument (that's the definition of passing a value), so changes it makes will be invisible to the caller.

    In your case, func (s Sample) Append(name string) modifies a copy.

    laher reminds us in the comments that using a value instead of pointer also means getting a copy, and respecting the immutable nature of an object::

    You'd want to use the non-pointer valueMethod when (for nstance) you're returning a [value derived from an] 'immutable' private property.

    See "Why are receivers pass by value in Go?":

    Can be useful if for instance you have a small immutable object. The caller can know for certain that this method doesn't modify it's receiver.
    They can't know this if the receiver is a pointer without reading the code first.

    0 讨论(0)
  • 2021-01-31 03:37

    Go slices are a tricky beast. Internally, a variable of slice type (like []int) looks like this:

    struct {
        data *int // pointer to the data area
        len  int
        cap  int
    }
    

    When you pass a slice to a function, this structure is passed by value, while the underlying data area (i.e. what data points to) is not copied. The builtin append() function modifies the data area (or generates a new one) and returns a new slice with updated len, data, and cap values. If you want to overwrite anything that is not part of the underlying data area, you need to pass a pointer to the slice or return a modified slice.

    0 讨论(0)
  • 2021-01-31 03:51

    While most of the answers here capture exactly what happens, I wanted to dig a bit into how that's happening. I started with a couple small snippets of code:

    1. pointer method

       package main
      
       import "fmt"
      
       type Bar struct{}
      
       func (b *Bar) Print() {
           fmt.Println("debosmit ray")
       }
      
       func main() {
           b := Bar{}
           b.Print()
       }
      
    2. value method

       package main
      
       import "fmt"
      
       type Bar struct{}
      
       func (b Bar) Print() {
           fmt.Println("debosmit ray")
       }
      
       func main() {
           b := Bar{}
           b.Print()
       }
      

    Then, I wanted to look at the assembly for just the files (generated using go tool compile -S filename.go > filename.S, for each of the files. Both the outputs are available here (should be available forever).

    Let's take a look at the output of diff pointer.S value.S (pointer -> has pointer method, value -> has value method).

    14,15c14,15
    < "".(*Bar).Print STEXT size=138 args=0x8 locals=0x58
    <   0x0000 00000 (bar.go:7) TEXT    "".(*Bar).Print(SB), ABIInternal, $88-8
    ---
    > "".Bar.Print STEXT size=138 args=0x0 locals=0x58
    >   0x0000 00000 (bar.go:7) TEXT    "".Bar.Print(SB), ABIInternal, $88-0
    24c24
    <   0x001d 00029 (bar.go:7) FUNCDATA    $0, gclocals·2a5305abe05176240e61b8620e19a815(SB)
    ---
    >   0x001d 00029 (bar.go:7) FUNCDATA    $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
    26c26
    <   0x001d 00029 (bar.go:7) FUNCDATA    $3, "".(*Bar).Print.stkobj(SB)
    ---
    >   0x001d 00029 (bar.go:7) FUNCDATA    $3, "".Bar.Print.stkobj(SB)
    126a127,200
    > "".(*Bar).Print STEXT dupok size=187 args=0x8 locals=0x58
    >   0x0000 00000 (<autogenerated>:1)    TEXT    "".(*Bar).Print(SB), DUPOK|WRAPPER|ABIInternal, $88-8
    >   0x0000 00000 (<autogenerated>:1)    MOVQ    (TLS), CX
    >   0x0009 00009 (<autogenerated>:1)    CMPQ    SP, 16(CX)
    >   0x000d 00013 (<autogenerated>:1)    PCDATA  $0, $-2
    >   0x000d 00013 (<autogenerated>:1)    JLS 154
    >   0x0013 00019 (<autogenerated>:1)    PCDATA  $0, $-1
    >   0x0013 00019 (<autogenerated>:1)    SUBQ    $88, SP
    >   0x0017 00023 (<autogenerated>:1)    MOVQ    BP, 80(SP)
    >   0x001c 00028 (<autogenerated>:1)    LEAQ    80(SP), BP
    >   0x0021 00033 (<autogenerated>:1)    MOVQ    32(CX), BX
    >   0x0025 00037 (<autogenerated>:1)    TESTQ   BX, BX
    >   0x0028 00040 (<autogenerated>:1)    JNE 165
    >   0x002a 00042 (<autogenerated>:1)    NOP
    >   0x002a 00042 (<autogenerated>:1)    FUNCDATA    $0, gclocals·1a65e721a2ccc325b382662e7ffee780(SB)
    >   0x002a 00042 (<autogenerated>:1)    FUNCDATA    $1, gclocals·2589ca35330fc0fce83503f4569854a0(SB)
    >   0x002a 00042 (<autogenerated>:1)    FUNCDATA    $3, "".(*Bar).Print.stkobj(SB)
    >   0x002a 00042 (<autogenerated>:1)    CMPQ    ""..this+96(SP), $0
    >   0x0030 00048 (<autogenerated>:1)    JEQ 148
    >   0x0032 00050 (<unknown line number>)    NOP
    >   0x0032 00050 (bar.go:8) XORPS   X0, X0
    >   0x0035 00053 (bar.go:8) MOVUPS  X0, ""..autotmp_13+64(SP)
    >   0x003a 00058 (bar.go:8) LEAQ    type.string(SB), AX
    >   0x0041 00065 (bar.go:8) MOVQ    AX, ""..autotmp_13+64(SP)
    >   0x0046 00070 (bar.go:8) LEAQ    ""..stmp_2(SB), AX
    >   0x004d 00077 (bar.go:8) MOVQ    AX, ""..autotmp_13+72(SP)
    >   0x0052 00082 (<unknown line number>)    NOP
    >   0x0052 00082 ($GOROOT/src/fmt/print.go:274) MOVQ    os.Stdout(SB), AX
    >   0x0059 00089 ($GOROOT/src/fmt/print.go:274) LEAQ    go.itab.*os.File,io.Writer(SB), CX
    >   0x0060 00096 ($GOROOT/src/fmt/print.go:274) MOVQ    CX, (SP)
    >   0x0064 00100 ($GOROOT/src/fmt/print.go:274) MOVQ    AX, 8(SP)
    >   0x0069 00105 ($GOROOT/src/fmt/print.go:274) LEAQ    ""..autotmp_13+64(SP), AX
    >   0x006e 00110 ($GOROOT/src/fmt/print.go:274) MOVQ    AX, 16(SP)
    >   0x0073 00115 ($GOROOT/src/fmt/print.go:274) MOVQ    $1, 24(SP)
    >   0x007c 00124 ($GOROOT/src/fmt/print.go:274) MOVQ    $1, 32(SP)
    >   0x0085 00133 ($GOROOT/src/fmt/print.go:274) PCDATA  $1, $1
    >   0x0085 00133 ($GOROOT/src/fmt/print.go:274) CALL    fmt.Fprintln(SB)
    >   0x008a 00138 (bar.go:8) MOVQ    80(SP), BP
    >   0x008f 00143 (bar.go:8) ADDQ    $88, SP
    >   0x0093 00147 (bar.go:8) RET
    >   0x0094 00148 (<autogenerated>:1)    CALL    runtime.panicwrap(SB)
    >   0x0099 00153 (<autogenerated>:1)    XCHGL   AX, AX
    >   0x009a 00154 (<autogenerated>:1)    NOP
    >   0x009a 00154 (<autogenerated>:1)    PCDATA  $1, $-1
    >   0x009a 00154 (<autogenerated>:1)    PCDATA  $0, $-2
    >   0x009a 00154 (<autogenerated>:1)    CALL    runtime.morestack_noctxt(SB)
    >   0x009f 00159 (<autogenerated>:1)    PCDATA  $0, $-1
    >   0x009f 00159 (<autogenerated>:1)    NOP
    >   0x00a0 00160 (<autogenerated>:1)    JMP 0
    >   0x00a5 00165 (<autogenerated>:1)    LEAQ    96(SP), DI
    >   0x00aa 00170 (<autogenerated>:1)    CMPQ    (BX), DI
    >   0x00ad 00173 (<autogenerated>:1)    JNE 42
    >   0x00b3 00179 (<autogenerated>:1)    MOVQ    SP, (BX)
    >   0x00b6 00182 (<autogenerated>:1)    JMP 42
    >   0x0000 65 48 8b 0c 25 00 00 00 00 48 3b 61 10 0f 86 87  eH..%....H;a....
    >   0x0010 00 00 00 48 83 ec 58 48 89 6c 24 50 48 8d 6c 24  ...H..XH.l$PH.l$
    >   0x0020 50 48 8b 59 20 48 85 db 75 7b 48 83 7c 24 60 00  PH.Y H..u{H.|$`.
    >   0x0030 74 62 0f 57 c0 0f 11 44 24 40 48 8d 05 00 00 00  tb.W...D$@H.....
    >   0x0040 00 48 89 44 24 40 48 8d 05 00 00 00 00 48 89 44  .H.D$@H......H.D
    >   0x0050 24 48 48 8b 05 00 00 00 00 48 8d 0d 00 00 00 00  $HH......H......
    >   0x0060 48 89 0c 24 48 89 44 24 08 48 8d 44 24 40 48 89  H..$H.D$.H.D$@H.
    >   0x0070 44 24 10 48 c7 44 24 18 01 00 00 00 48 c7 44 24  D$.H.D$.....H.D$
    >   0x0080 20 01 00 00 00 e8 00 00 00 00 48 8b 6c 24 50 48   .........H.l$PH
    >   0x0090 83 c4 58 c3 e8 00 00 00 00 90 e8 00 00 00 00 90  ..X.............
    >   0x00a0 e9 5b ff ff ff 48 8d 7c 24 60 48 39 3b 0f 85 77  .[...H.|$`H9;..w
    >   0x00b0 ff ff ff 48 89 23 e9 6f ff ff ff                 ...H.#.o...
    >   rel 5+4 t=17 TLS+0
    >   rel 61+4 t=16 type.string+0
    >   rel 73+4 t=16 ""..stmp_2+0
    >   rel 85+4 t=16 os.Stdout+0
    >   rel 92+4 t=16 go.itab.*os.File,io.Writer+0
    >   rel 134+4 t=8 fmt.Fprintln+0
    >   rel 149+4 t=8 runtime.panicwrap+0
    >   rel 155+4 t=8 runtime.morestack_noctxt+0
    139,143c213,217
    < go.info."".(*Bar).Print$abstract SDWARFINFO dupok size=26
    <   0x0000 04 2e 28 2a 42 61 72 29 2e 50 72 69 6e 74 00 01  ..(*Bar).Print..
    <   0x0010 01 11 62 00 00 00 00 00 00 00                    ..b.......
    <   rel 0+0 t=24 type.*"".Bar+0
    <   rel 21+4 t=29 go.info.*"".Bar+0
    ---
    > go.info."".Bar.Print$abstract SDWARFINFO dupok size=23
    >   0x0000 04 2e 42 61 72 2e 50 72 69 6e 74 00 01 01 11 62  ..Bar.Print....b
    >   0x0010 00 00 00 00 00 00 00                             .......
    >   rel 0+0 t=24 type."".Bar+0
    >   rel 18+4 t=29 go.info."".Bar+0
    297c371,392
    < type."".Bar SRODATA size=96
    ---
    > type..namedata.*func(main.Bar)- SRODATA dupok size=18
    >   0x0000 00 00 0f 2a 66 75 6e 63 28 6d 61 69 6e 2e 42 61  ...*func(main.Ba
    >   0x0010 72 29                                            r)
    > type.*func("".Bar) SRODATA dupok size=56
    >   0x0000 08 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00  ................
    >   0x0010 7f 95 9a 2f 08 08 08 36 00 00 00 00 00 00 00 00  .../...6........
    >   0x0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
    >   0x0030 00 00 00 00 00 00 00 00                          ........
    >   rel 24+8 t=1 runtime.memequal64·f+0
    >   rel 32+8 t=1 runtime.gcbits.01+0
    >   rel 40+4 t=5 type..namedata.*func(main.Bar)-+0
    >   rel 48+8 t=1 type.func("".Bar)+0
    > type.func("".Bar) SRODATA dupok size=64
    >   0x0000 08 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00  ................
    >   0x0010 b4 2e bc 27 02 08 08 33 00 00 00 00 00 00 00 00  ...'...3........
    >   0x0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
    >   0x0030 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
    >   rel 32+8 t=1 runtime.gcbits.01+0
    >   rel 40+4 t=5 type..namedata.*func(main.Bar)-+0
    >   rel 44+4 t=6 type.*func("".Bar)+0
    >   rel 56+8 t=1 type."".Bar+0
    > type."".Bar SRODATA size=112
    303c398,399
    <   0x0050 00 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00  ................
    ---
    >   0x0050 00 00 00 00 01 00 01 00 10 00 00 00 00 00 00 00  ................
    >   0x0060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
    309a406,409
    >   rel 96+4 t=5 type..namedata.Print.+0
    >   rel 100+4 t=25 type.func()+0
    >   rel 104+4 t=25 "".(*Bar).Print+0
    >   rel 108+4 t=25 "".Bar.Print+0
    320a421,423
    > ""..stmp_2 SRODATA size=16
    >   0x0000 00 00 00 00 00 00 00 00 0c 00 00 00 00 00 00 00  ................
    >   rel 0+8 t=1 go.string."debosmit ray"+0
    325,326c428,429
    < gclocals·2a5305abe05176240e61b8620e19a815 SRODATA dupok size=9
    <   0x0000 01 00 00 00 01 00 00 00 00                       .........
    ---
    > gclocals·33cdeccccebe80329f1fdbee7f5874cb SRODATA dupok size=8
    >   0x0000 01 00 00 00 00 00 00 00                          ........
    329c432
    < "".(*Bar).Print.stkobj SRODATA size=24
    ---
    > "".Bar.Print.stkobj SRODATA size=24
    333,334d435
    < gclocals·33cdeccccebe80329f1fdbee7f5874cb SRODATA dupok size=8
    <   0x0000 01 00 00 00 00 00 00 00                          ........
    338a440,447
    > gclocals·1a65e721a2ccc325b382662e7ffee780 SRODATA dupok size=10
    >   0x0000 02 00 00 00 01 00 00 00 01 00                    ..........
    > gclocals·2589ca35330fc0fce83503f4569854a0 SRODATA dupok size=10
    >   0x0000 02 00 00 00 02 00 00 00 00 00                    ..........
    > "".(*Bar).Print.stkobj SRODATA dupok size=24
    >   0x0000 01 00 00 00 00 00 00 00 f0 ff ff ff ff ff ff ff  ................
    >   0x0010 00 00 00 00 00 00 00 00                          ........
    >   rel 16+8 t=1 type.[1]interface {}+0
    

    Here, it is evident that for the value method case:

    1. a copy of b was created due to the b.Print() invocation
    2. a copy of the string rel 0+8 t=1 go.string."debosmit ray"+0 was set on the copied struct.

    Therefore, this further concretizes that when you use value pointers:

    1. a copy of the object is made when you invoke methods on that object
    2. any mutations to internal state, will only reflect on that copy of the object
    0 讨论(0)
  • 2021-01-31 03:54

    Go passes arguments by value, not by reference, unless you use a pointer. So inside the function, you're not modifying s in any outer scope if you simply pass by value. However, when you pass a pointer, you're able to modify the "real" variable rather than just the copy that exists inside the function.

    0 讨论(0)
提交回复
热议问题