即使我使用指向类型的指针来更新对象,但对象仍然没有被更新。

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

My object is not updated even if I use the pointer to a type to update it

问题

我将为您翻译以下内容:

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

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

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

type Person struct {
    name string
}

// 实现 Individual 接口的函数
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) // 将 Person 添加到切片
    p1 := individuals[0]                      // 从切片中检索唯一的 Person
    p1.SetName("Peter")                       // 更改名称
    fmt.Println(p1)                           // 应该打印 "Peter",但打印的是 "Steve"
    fmt.Println(person)                       // 应该打印 "Peter",但打印的是 "Steve"
}

问题出在SetName方法中。在该方法中,您使用了p.GetName()来获取名称的指针,并尝试将其更改为新的名称。但是,由于GetName方法返回的是Person结构体中name字段的指针,所以实际上您只是修改了p1指向的内存中的值,并没有修改person的值。

要解决这个问题,您可以将SetName方法的接收者类型更改为指针类型,以便直接修改person的值。修改后的代码如下:

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

这样修改后,您将能够正确地更改person的名称,并且打印出的结果将是"Peter"

英文:

I store some Individual objects in a slice. Before appending it to the slice I print the name of the Individual object.

After I have stored it in the slice, I then retrieve it as a pointer and want to change the name to "Peter", but the change does not work since it still prints "Steve". Why?

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"
}

答案1

得分: 11

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

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

所以在你的例子中:

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

当调用SetName()时,会创建一个Person的副本。在SetName()内部,你获取到的是副本name字段的地址,然后进行修改。(实际上是副本的副本,稍后会详细解释...)

解决方法是使用指针接收者:

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

从此时起,只有*Person实现了Individual接口,所以在追加时要使用指针:

individuals = append(individuals, &person)

这有点棘手,因为即使这样仍然不起作用。为什么呢?

这是因为Person.GetName()仍然具有非指针接收者:

func (p Person) GetName() *string {
    return &p.name
}

所以当从SetName()调用GetName()时,又会再次创建一个副本,并且GetName()将返回副本name字段的地址,而SetName()只会修改为调用GetName()创建的副本。

所以为了使所有的工作正常,你还必须对GetName()使用指针接收者:

func (p *Person) GetName() *string {
    return &p.name
}

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

{Steve}
&{Peter}
{Peter}

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

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

这就是所需的全部内容。

英文:

Whenever a method wants to modify the receiver, it must be a pointer to the value; the method must have a pointer receiver.

If a method has a non-pointer receiver, a copy will be made and passed when that method is called. From that point, no matter what you do with the receiver, you can only modify the copy, and not the original.

So in your example:

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

When SetName() is called, a copy is made of the Person. Inside SetName() you obtain the address of the name field of the copy, which you modify. (Actually, a copy of the copy, more on this later...)

Solution: use a pointer receiver:

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

From this point on, only *Person implements Individual, so use a pointer when appending:

individuals = append(individuals, &person)

It's tricky, because after this it sill won't work. Why is that?

It is because the Person.GetName() still has a non-pointer receiver:

func (p Person) GetName() *string {
	return &p.name
}

So when GetName() is called from SetName(), a copy will be made again, and GetName() will return the address of the name field of the copy, and SetName() will only modify the copy created for calling GetName().

So to make all work, you also have to use pointer receiver for GetName():

func (p *Person) GetName() *string {
	return &p.name
}

And now it's working, output (try it on the Go Playground):

{Steve}
&{Peter}
{Peter}

But know that the easiest and recommended way is simply:

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

That's all it takes.

答案2

得分: 3

修复这个问题的两个方法:

  • 你需要通过一个“指针”方法来附加方法,否则名称只会在方法内部更改。
  • 你需要对实际的Person变量使用指针,因为它们需要实现一个接口。
type Individual interface {
    GetName() *string
    SetName(name string)
}

type Person struct {
    name string
}

// 实现 Individual 接口的函数
func (p Person) GetName() *string {
    return &p.name
}

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

var individuals []Individual

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

    individuals = append(individuals, person) // 将 Person 添加到切片中
    p1 := individuals[0]                      // 从切片中获取唯一的 Person
    p1.SetName("Peter")                       // 更改名称
    fmt.Println(p1)                           // 输出 "Steve"
    fmt.Println(person)                       // 输出 "Steve"
}

Go Playground 上有一个示例。

英文:

Two things to fix this:

  • You need the methods attach via a "pointer" method, otherwise the name is only changed inside the method.
  • You need to use pointers for the actual Person variables, since they need to implement an interface

<!-- language: go -->

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 &amp;p.name
}

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


var individuals []Individual

func main() {
    person := &amp;Person{name: &quot;Steve&quot;}
    fmt.Println(person)

    individuals = append(individuals, person) // append Person to slice
    p1 := individuals[0]     // Retrieve the only Person from slice
    p1.SetName(&quot;Peter&quot;)      // Change name
    fmt.Println(p1)          // Prints &quot;Steve&quot;
    fmt.Println(person)      // Prints &quot;Steve&quot;
}

Example on Go Playground.

答案3

得分: 0

请参考 https://play.golang.org/p/eg8oYDV6Xx。p1和p2已经是地址,你不需要它们的地址(地址的地址),只需打印p1和p2 - 你会发现它们是相同的。

英文:

See https://play.golang.org/p/eg8oYDV6Xx p1 and p2 are already addresses, you don't need to their address (address of address), just print p1 and p2 - they'll be the same as you can see.

huangapple
  • 本文由 发表于 2017年5月10日 19:07:30
  • 转载请务必保留本文链接:https://go.coder-hub.com/43890706.html
匿名

发表评论

匿名网友

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

确定