即使使用指向类型的指针来更新它,我的对象也不会更新

Rox*_*Rox 4 methods pointers go slice

我将一些Individual对象存储在切片中。在将其附加到切片之前,我先打印Individual对象的名称。

将其存储在切片中之后,我将其检索为指针,并希望将名称更改为"Peter",但是更改仍然有效,因为它仍然可以打印"Steve"。为什么?

type Individual interface {
    GetName() *string
    SetName(name string)
}

type Person struct {
    name string
}

// Implement functions of the Individual interface
func (p Person) GetName() *string  {
    return &p.name
}

func (p Person) SetName(newName string)  {
    name := p.GetName();
    *name = newName
}


var individuals []Individual

func main() {
    person := Person{name: "Steve"}
    fmt.Println(person)

    individuals = append(individuals, person) // append Person to slice
    p1 := individuals[0]     // Retrieve the only Person from slice
    p1.SetName("Peter")      // Change name
    fmt.Println(p1)          // Should print "Peter", but prints "Steve"
    fmt.Println(person)      // Should print "Peter", but prints "Steve"
}
Run Code Online (Sandbox Code Playgroud)

icz*_*cza 5

每当方法想要修改接收者时,它都必须是指向该值的指针;该方法必须具有指针接收器。

如果某个方法具有非指针接收者,则在调用该方法时将创建并传递一个副本。从那时起,无论您对接收器做什么,都只能修改副本,而不能修改原始副本。

因此,在您的示例中:

func (p Person) SetName(newName string)  {
    name := p.GetName();
    *name = newName
}
Run Code Online (Sandbox Code Playgroud)

SetName()被调用时,一份拷贝的Person。在内部,SetName()您可以获取要修改name副本字段的地址。(实际上,是该副本的副本,稍后会有更多信息……)

解决方案:使用指针接收器:

func (p *Person) SetName(newName string)  {
    name := p.GetName();
    *name = newName
}
Run Code Online (Sandbox Code Playgroud)

从这一点开始,仅*Person实现Individual,因此在追加时使用指针:

individuals = append(individuals, &person)
Run Code Online (Sandbox Code Playgroud)

这很棘手,因为在此之后它将不再起作用。这是为什么?

这是因为Person.GetName()仍然有一个非指针接收器:

func (p Person) GetName() *string {
    return &p.name
}
Run Code Online (Sandbox Code Playgroud)

因此,当GetName()从中调用时SetName(),将再次创建一个副本,GetName()并将返回name副本字段的地址,并且SetName()仅修改为调用创建的副本GetName()

因此,要使所有工作正常进行,还必须将指针接收器用于GetName()

func (p *Person) GetName() *string {
    return &p.name
}
Run Code Online (Sandbox Code Playgroud)

现在它可以工作了,输出(在Go Playground上尝试):

{Steve}
&{Peter}
{Peter}
Run Code Online (Sandbox Code Playgroud)

但要知道,最简单和推荐的方法很简单:

func (p *Person) SetName(newName string) {
    p.name = newName
}
Run Code Online (Sandbox Code Playgroud)

这就是全部。

  • 我认为`GetName` 返回`string` 按值会更惯用吗? (2认同)