英文:
go: using pointer allows changing the contents of a struct. Why?
问题
考虑以下示例。我不完全理解在“后台”发生了什么,并寻求解释。当我从主函数调用AddToEntry
时,这个版本似乎会复制结构体Foo
。对吗?我如何在代码中“证明”这一点?
当Go复制结构体时,我只是操作结构体的副本,当我回到main
函数时,我看到的是原始的结构体,就像之前一样?
当我期望一个指针(请参见代码中的注释)时,一切都很好,我的结构体没有被复制。我如何避免这种“错误”?我如何确保我没有复制结构体?是否有可能在编译时/运行时进行检查,或者我必须小心处理?
package main
import (
"fmt"
)
type Foo struct {
Entry []string
}
func MakeFoo() Foo {
a := Foo{}
a.Entry = append(a.Entry, "first")
return a
}
// 如果我将 (f Foo) 改为 (f *Foo),我会得到“期望”的结果
func (f Foo) AddToEntry() {
f.Entry = append(f.Entry, "second")
}
func main() {
f := MakeFoo()
fmt.Println(f) // {[first]}
f.AddToEntry()
fmt.Println(f) // {[first]}
}
英文:
Consider the following example. I don't fully understand what happens "in the background" and seek an explanation. This version seems to make a copy of the struct Foo
when I call AddToEntry
from the main function. Right? How can I "proof" this in the code?
When go makes a copy of the struct, I am just manipulating the copy of the struct and when I get back to the main
function I see the original as before?
When I expect a pointer (see comment in the code), everything is fine, my struct is not copied. How can avoid this kind of "error"? How can I make sure I am not copying the struct? Is there a possible compile time/run time check for that, or do I have be careful?
package main
import (
"fmt"
)
type Foo struct {
Entry []string
}
func MakeFoo() Foo {
a:=Foo{}
a.Entry = append(a.Entry,"first")
return a
}
// if I change (f Foo) to (f *Foo), I get
// the "desired" result
func (f Foo) AddToEntry() {
f.Entry = append(f.Entry,"second")
}
func main() {
f:=MakeFoo()
fmt.Println(f) // {[first]}
f.AddToEntry()
fmt.Println(f) // {[first]}
}
答案1
得分: 8
你的方法签名是 func (f Foo) AddToEntry()
。方法的工作方式是,f.AddToEntry()
等同于:
g := Foo.AddToEntry
g(f)
接收器只是另一个参数。为什么这很重要?当你将一个结构体传递给函数并在函数中修改它时会发生什么?在 C、Go 和其他按值传递的语言中,作为参数传递的结构体只是一个副本。因此,你无法修改原始结构体,只能返回新的结构体。
当你定义 func (f *Foo) AddToEntry()
时,你将接收器(第一个参数)定义为指针。显然,给定一个指针,你可以修改原始结构体。隐藏的是,当你在 Go 中访问一个结构体时,你其实是在隐式引用它。换句话说,在 Go 中,(*ptrFoo).Entry
和 ptrFoo.Entry
是相同的。
所以问题在于,对于那些不习惯 Go 的人来说,语法隐藏了一些正在发生的事情。在 C 中,除非你传递一个指针,否则你永远无法编辑一个结构体。在 Go 中也是如此。你需要使用指针接收器才能修改你接收到的内容。
英文:
Your method signature is func (f Foo) AddToEntry()
. The way methods work, f.AddToEntry()
is is the same as:
g := Foo.AddToEntry
g(f)
The receiver is just another parameter. Why is this important? What happens when you pass a struct and modify it in a function? In C, Go, and other pass by value languages, the struct given in the parameter is only a copy. Therefore, you can not modify the original. Only return the new struct.
When you define func (f *Foo) AddToEntry()
, you are defining the receiver, the first parameter, as a pointer. Obviously, given a pointer, you can modify the original struct. What is hidden is that you are implicitly referencing when you access a struct in Go. To put it another way, (*ptrFoo).Entry
is the same as ptrFoo.Entry
in Go.
So the issue here is that for those unaccustomed to go, the syntax is hiding some of what is going on. In C, you would never be able to edit a struct unless you passed a pointer to it. The same happens in Go. You need to use a pointer receiver in order to modify what you are receiving.
答案2
得分: 5
你读过这个Go文档吗?
答案3
得分: 2
这里的简短答案是不行,你不能在编译时或运行时进行检查,你只需要小心就可以了。一旦你对Go有了一些了解,这就变得自然了。
英文:
> How can I make sure I am not copying the struct? Is there a possible
> compile time/run time check for that, or do I have be careful?
The short answer here is that no , you can't do a compile-time or run-time(1) check
for this - you just have to be careful. Once you get a bit familiar with go, this becomes natural.
(1) <sub><sup>
Technically your function could query whether the type is a pointer or not with the type switch, but if you remember to do that, you'll also remember to make the parameter a pointer.</sub></sup>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论