使用指针可以改变结构体的内容。为什么?

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

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).EntryptrFoo.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文档吗?

我应该在值上还是指针上定义方法?

方法:指针 vs. 值

Go编程语言规范

英文:

Have you read this Go documentation?

Should I define methods on values or pointers?

Methods: Pointers vs. Values

The Go Programming Language Specification

答案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>

huangapple
  • 本文由 发表于 2012年8月6日 02:37:59
  • 转载请务必保留本文链接:https://go.coder-hub.com/11819029.html
匿名

发表评论

匿名网友

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

确定