将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.
如果我有结构类型 也就是说, p>
和 p>
例如,是否曾经复制过 嵌入式字段? p>
编辑:我还应该提到嵌入式结构 A code>用作指针(具有指针接收器 仅构造函数返回
* A code>等),将结构类型
B code>嵌入为
B code>与
*之间有什么区别? B code>? p>
类型B结构{...}
类型A有什么区别 结构{
B
// ...
}
code> pre>
B型结构 {...}
type A struct {
* B
// ...
}
code> pre>
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
changesstructB
changing the value of the copied version
structB
instructAVal
leavesstructB
unaffected (it is still5
, even after42
was assigned toaVal
)
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!