使用指针或不使用指针定义golang结构函数

Jac*_*cob 25 go

有人可以向我解释为什么在执行此操作时附加到数组会起作用:

func (s *Sample) Append(name string) {
    d := &Stuff{
        name: name,
    }
    s.data = append(s.data, d)
}
Run Code Online (Sandbox Code Playgroud)

完整代码在这里

但不是在你这样做的时候:

func (s Sample) Append(name string) {
    d := &Stuff{
        name: name,
    }
    s.data = append(s.data, d)
}
Run Code Online (Sandbox Code Playgroud)

你有什么理由想要使用第二个例子吗?

Von*_*onC 46

FAQ中所述

我应该在值或指针上定义方法吗?

func (s *MyStruct) pointerMethod() { } // method on pointer
func (s MyStruct)  valueMethod()   { } // method on value
Run Code Online (Sandbox Code Playgroud)

首先,最重要的是,该方法是否需要修改接收器?如果是,接收器必须是指针.(切片和贴图充当引用,因此它们的故事更加微妙,但是例如在方法中更改切片的长度时接收器仍然必须是指针.)

在上面的例子中,如果pointerMethod修改S的领域,主叫方会看到这些变化,但valueMethod是带一个呼叫者的说法副本(这是传递一个值的定义),所以改变它使得将是不可见的,呼叫者.

在您的情况下,func (s Sample) Append(name string)修改副本.

laher 在评论中提醒我们使用值代替指针也意味着获取副本,并尊重对象的不可变性质::

你想要valueMethod在(对于nstance)返回一个[从一个'不可变的'私有属性派生的值时使用非指针.

请参阅" 为什么接收者在Go中按值传递? ":

例如,如果您有一个小的不可变对象,则可能很有用.调用者可以确定该方法不会修改它的接收者.
如果接收器是指针而没有首先读取代码,则他们无法知道这一点.

  • @laher 好点。我已将您的评论包含在答案中以获得更多可见性,并且我添加了一些链接/参考。 (2认同)
  • 不错,现在看起来不错。我想知道这个问题已经有一段时间了,并且我有兴趣了解任何其他用例。我想你可能会说它避免了对显式“防御性副本”的需要,但这只是同一不变性点的一部分。干杯@VonC (2认同)

Deb*_*Ray 10

虽然这里的大多数答案都准确地描述了发生的情况,但我想深入了解一下如何/为什么发生的。我从几个小代码片段开始:

\n
    \n
  1. 指针法

    \n
     package main\n\n import "fmt"\n\n type Bar struct{}\n\n func (b *Bar) Print() {\n     fmt.Println("debosmit ray")\n }\n\n func main() {\n     b := Bar{}\n     b.Print()\n }\n
    Run Code Online (Sandbox Code Playgroud)\n
  2. \n
  3. 价值法

    \n
     package main\n\n import "fmt"\n\n type Bar struct{}\n\n func (b Bar) Print() {\n     fmt.Println("debosmit ray")\n }\n\n func main() {\n     b := Bar{}\n     b.Print()\n }\n
    Run Code Online (Sandbox Code Playgroud)\n
  4. \n
\n

然后,我想查看文件的程序集(使用go tool compile -S filename.go > filename.S, 为每个文件生成。两个输出都可以在此处获得)使用(应该永远可用)。

\n

让我们看一下输出diff pointer.S value.S(指针 -> 有指针方法,值 -> 有值方法)的输出。

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

在这里,对于值方法的情况很明显:

\n
    \n
  1. b由于以下原因创建了 的副本b.Print()调用
  2. \n
  3. 字符串的副本rel 0+8 t=1 go.string."debosmit ray"+0在复制的结构上设置了
  4. \n
\n

因此,这进一步具体化了当您使用值指针时:

\n
    \n
  1. 当您调用该对象上的方法时,会创建该对象的副本
  2. \n
  3. 内部状态的任何突变只会反映在该对象的副本上
  4. \n
\n


fuz*_*fuz 9

去切片是一个棘手的野兽.在内部,切片类型的变量(如[]int)看起来像这样:

struct {
    data *int // pointer to the data area
    len  int
    cap  int
}
Run Code Online (Sandbox Code Playgroud)

将切片传递给函数时,此结构将按传递,而data不会复制基础数据区域(即指向的内容).内建append()函数修改data区域(或产生新的一个),并返回一个新的切片具有更新len,datacap的值.如果要覆盖不属于基础数据区域的任何内容,则需要将指针传递给切片或返回修改后的切片.


Tod*_*obs 7

Go 按值传递参数,而不是按引用传递参数,除非您使用指针。因此,在函数内部,如果您只是按值传递,则不会在任何外部作用域中修改s 。但是,当您传递指针时,您可以修改“真实”变量,而不仅仅是函数内部存在的副本。