英文:
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 &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) // append Person to slice
p1 := individuals[0] // Retrieve the only Person from slice
p1.SetName("Peter") // Change name
fmt.Println(p1) // Prints "Steve"
fmt.Println(person) // Prints "Steve"
}
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论