为什么在函数调用后我的字段被截断了?

huangapple go评论76阅读模式
英文:

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]

huangapple
  • 本文由 发表于 2015年4月9日 09:03:36
  • 转载请务必保留本文链接:https://go.coder-hub.com/29527970.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定