英文:
Why do my fields get truncated after the function call?
问题
我已经尝试了两个小时的一切办法。进入主函数后,我们立即到达第24-26行:
prompter.Define(&Field{"name"})
prompter.Define(&Field{"age"})
定义函数:
fmt.Printf("fields: %+v\n", c.fields)
c.fields = append(c.fields, f)
fmt.Printf("fields: %+v\n", c.fields)
在函数调用之后,c.fields
数组又变为空了!输出结果如下:
fields: []
fields: [0x1040a120]
fields: []
fields: [0x1040a130]
英文:
http://play.golang.org/p/xFBSZta2CL
I have been trying everything for 2 hours now. Going into the main function, we immediately get to line 24-26:
prompter.Define(&Field{"name"})
prompter.Define(&Field{"age"})
The define function:
fmt.Printf("fields: %+v\n", c.fields)
c.fields = append(c.fields, f)
fmt.Printf("fields: %+v\n", c.fields)
After the function call, the c.fields
array is empty again!!! Output:
fields: []
fields: [0x1040a120]
fields: []
fields: [0x1040a130]
答案1
得分: 7
Go编程语言
常见问题(FAQ)
我应该在值上定义方法还是指针上定义方法?
func (s *MyStruct) pointerMethod() { } // 指针上的方法
func (s MyStruct) valueMethod() { } // 值上的方法
对于不习惯使用指针的程序员来说,这两个示例之间的区别可能会令人困惑,但实际情况非常简单。在类型上定义方法时,接收器(上述示例中的s
)的行为就像它是方法的参数一样。因此,将接收器定义为值还是指针是同一个问题,就像函数参数应该是值还是指针一样。有几个考虑因素。
首先,最重要的是,方法是否需要修改接收器?如果需要修改接收器,则接收器必须是指针。(切片和映射作为引用,因此它们的情况要稍微复杂一些,但是例如要在方法中更改切片的长度,接收器仍然必须是指针。)在上面的示例中,如果pointerMethod
修改s
的字段,调用者将看到这些更改,但是valueMethod
使用调用者参数的副本进行调用(这就是传递值的定义),因此它所做的更改对调用者是不可见的。
顺便说一下,指针接收器与Java中的情况完全相同,尽管在Java中指针是隐藏在底层的;而Go中的值接收器则是不寻常的。
其次是效率的考虑。如果接收器很大,比如一个大的结构体,那么使用指针接收器会更加高效。
接下来是一致性的考虑。如果类型的某些方法必须具有指针接收器,那么其余的方法也应该具有指针接收器,以便无论如何使用该类型,方法集都是一致的。有关详细信息,请参阅有关方法集的部分。
对于基本类型、切片和小型结构体等类型,值接收器非常便宜,因此除非方法的语义要求指针,否则值接收器是高效和清晰的。
在Go中,所有参数和返回值都是按值传递的。接收器也是按值传递的。使用指针接收器来更改值。例如,
package main
import (
"fmt"
)
type Prompter interface {
Define(f *Field)
}
type Field struct {
Key string
}
type Provider interface {
Prompt(Prompter)
}
var providers = []Provider{
MyProvider{},
}
type MyProvider struct{}
func (p MyProvider) Prompt(prompter Prompter) {
prompter.Define(&Field{"name"})
prompter.Define(&Field{"age"})
}
type CliPrompter struct {
fields []*Field
}
func NewCliPrompter() *CliPrompter {
return &CliPrompter{
fields: make([]*Field, 0, 100),
}
}
func (c *CliPrompter) Define(f *Field) {
fmt.Printf("fields: %+v\n", c.fields)
c.fields = append(c.fields, f)
fmt.Printf("fields: %+v\n", c.fields)
}
func main() {
providers[0].Prompt(NewCliPrompter())
}
输出:
fields: []
fields: [0x1040a120]
fields: [0x1040a120]
fields: [0x1040a120 0x1040a130]
英文:
> The Go Programming Language
>
> Frequently Asked Questions (FAQ)
>
> Should I define methods on values or pointers?
>
> func (s *MyStruct) pointerMethod() { } // method on pointer
> func (s MyStruct) valueMethod() { } // method on value
>
> For programmers unaccustomed to pointers, the distinction between
> these two examples can be confusing, but the situation is actually
> very simple. When defining a method on a type, the receiver (s in the
> above examples) behaves exactly as if it were an argument to the
> method. Whether to define the receiver as a value or as a pointer is
> the same question, then, as whether a function argument should be a
> value or a pointer. There are several considerations.
>
> 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.
>
> By the way, pointer receivers are identical to the situation in Java,
> although in Java the pointers are hidden under the covers; it's Go's
> value receivers that are unusual.
>
> Second is the consideration of efficiency. If the receiver is large, a
> big struct for instance, it will be much cheaper to use a pointer
> receiver.
>
> Next is consistency. If some of the methods of the type must have
> pointer receivers, the rest should too, so the method set is
> consistent regardless of how the type is used. See the section on
> method sets for details.
>
> For types such as basic types, slices, and small structs, a value
> receiver is very cheap so unless the semantics of the method requires
> a pointer, a value receiver is efficient and clear.
In Go, all arguments and return values are passed by value. Receivers are passed by value. Use a pointer receiver to change the value. For example,
package main
import (
"fmt"
)
type Prompter interface {
Define(f *Field)
}
type Field struct {
Key string
}
type Provider interface {
Prompt(Prompter)
}
var providers = []Provider{
MyProvider{},
}
type MyProvider struct{}
func (p MyProvider) Prompt(prompter Prompter) {
prompter.Define(&Field{"name"})
prompter.Define(&Field{"age"})
}
type CliPrompter struct {
fields []*Field
}
func NewCliPrompter() *CliPrompter {
return &CliPrompter{
fields: make([]*Field, 0, 100),
}
}
func (c *CliPrompter) Define(f *Field) {
fmt.Printf("fields: %+v\n", c.fields)
c.fields = append(c.fields, f)
fmt.Printf("fields: %+v\n", c.fields)
}
func main() {
providers[0].Prompt(NewCliPrompter())
}
Output:
fields: []
fields: [0x1040a120]
fields: [0x1040a120]
fields: [0x1040a120 0x1040a130]
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论