将struct vs指针嵌入到用作指针的struct中

将struct vs指针嵌入到用作指针的struct中

问题描述:

If I have a struct type A which is used as a pointer (has pointer receivers only, the constructor returns *A, etc.), what is the difference between embedding a struct type B as B versus *B?

That is, what is the difference between

type B struct {...}
type A struct {
    B
    // ...
}

and

type B struct {...}
type A struct {
    *B
    // ...
}

For example, is there ever copying of the embedded field?

Edit: I should also mention that the embedded struct B only has pointer receivers.

如果我有结构类型 A code>用作指针(具有指针接收器 仅构造函数返回 * A code>等),将结构类型 B code>嵌入为 B code>与 *之间有什么区别? B code>? p>

也就是说, p>

 类型B结构{...} 
类型A有什么区别 结构{
 B 
 // ... 
} 
  code>  pre> 
 
 

和 p>

  B型结构 {...} 
type A struct {
 * B 
 // ... 
} 
  code>  pre> 
 
 

例如,是否曾经复制过 嵌入式字段? p>

编辑:我还应该提到嵌入式结构 B code>仅具有指针接收器。 p> div>

The zero values of the two structures are different, which can be a significant ergonomic difference.

Consider an embedded type

type B struct {
    X int
}

func (b *B) Print() { fmt.Printf("%d
", b.X) }

If we embed this directly as an object

type AObj struct {
    B
}

then the zero value of type AObj includes an embedded object of type B, which also has its zero value, and therefore we can safely

var aObj AObj
aObj.Print() // prints 0

But if we instead embed a pointer

type APtr struct {
    *B
}

the zero value of this struct has a nil pointer value, and we can't really use it directly.

var aPtr APtr
aPtr.Print() // panics

Objects get copied in hopefully the way you might expect. If you create a new AObj object, it gets a copy of the embedded B.

aObj2 := aObj
aObj.X = 1
aObj2.Print() // prints 0, because it has a copy

If you create a new APtr object, it gets a copy of the *B, which means it shares the underlying concrete object.

aPtr.B = &B{}
aPtr2 := aPtr
aPtr.X = 1
aPtr2.Print() // prints 1, because both objects point at the same B

Runnable example at https://play.golang.org/p/XmOgegwVFeE

Consider a simple example program. A structAPtr embeds a pointer, a structAVal embeds a struct structB directly:

package main

import "fmt"

type structB struct {
    foo int
}

type structAPtr struct {
    bar *structB
}

type structAVal struct {
    bar structB
}

func main() {
    // referencing bStruct
    b1 := structB{foo: 12}

    aPtr := structAPtr{bar: &b1}
    fmt.Println("Before assignment:")
    fmt.Printf("aPtr.bar.foo = %d, b.foo = %d
", aPtr.bar.foo, b1.foo)

    aPtr.bar.foo = 42
    fmt.Println("After assignment:")
    fmt.Printf("aPtr.bar.foo = %d, b.foo = %d
", aPtr.bar.foo, b1.foo)

    // copying bStruct
    b2 := structB{foo: 12}

    aVal := structAVal{bar: b2}
    fmt.Println("Before assignment:")
    fmt.Printf("aVal.bar.foo = %d, b.foo = %d
", aVal.bar.foo, b2.foo)

    aVal.bar.foo = 42
    fmt.Println("After assignment:")
    fmt.Printf("aVal.bar.foo = %d, b.foo = %d
", aVal.bar.foo, b2.foo)
}

The int structB.foo is used to demonstrate whether structB changes when manipulated inside of structAPtr or structAVal.

This program outputs:

Before assignment:
aPtr.bar.foo = 12, b.foo = 12
After assignment:
aPtr.bar.foo = 42, b.foo = 42 <------------ both changed
Before assignment:
aVal.bar.foo = 12, b.foo = 12
After assignment:
aVal.bar.foo = 42, b.foo = 12 <------------ only assignee changed

Looking at the result shows:

  • changing the value of the pointer to structB changes structB

  • changing the value of the copied version structB in structAVal leaves structB unaffected (it is still 5, even after 42 was assigned to aVal)


Edit:

If your structB has only pointer receivers anyways, the intended behavior is probably such that changing structB in strucA updated both of them. That's scenario 1 in my example and requires a pointer for sure. From A Tour of Go:

Methods with pointer receivers can modify the value to which the receiver points [...]. Since methods often need to modify their receiver, pointer receivers are more common than value receivers.


Hope that helps!