为什么我无法在Golang中的结构体属性中追加切片?

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

Why can't I append to a slice that's the property of a struct in golang?

问题

我正在尝试将一个值追加到 Golang 切片中,如果在第一个方法中调用该代码,它可以正常工作,但如果该方法调用另一个方法,代码似乎会失败。

以下是示例代码(Test3 是我最初尝试的):

package main

import (
	"fmt"
)

// 这个可以正常工作

type Test1 struct {
	all []int
}

func (c Test1) run() []int {
	for i := 0; i < 2; i++ {
		c.all = append(c.all, i)
	}
	return c.all
}

// 这个可以正常工作

var gloabl_all []int

type Test2 struct{}

func (c Test2) run() []int {
	c.combo()
	return gloabl_all
}

func (c Test2) combo() {
	for i := 0; i < 2; i++ {
		gloabl_all = append(gloabl_all, i)
	}
}

// 这个不行

type Test3 struct {
	all []int
}

func (c Test3) run() []int {
	c.combo()
	return c.all
}

func (c Test3) combo() {
	for i := 0; i < 2; i++ {
		c.all = append(c.all, i)
		fmt.Println("Test3 step", i+1, c.all)
	}
}

func main() {
	test1 := &Test1{}
	fmt.Println("Test1 final:", test1.run(), "\n")

	test2 := &Test2{}
	fmt.Println("Test2 final:", test2.run(), "\n")

	test3 := &Test3{}
	fmt.Println("Test3 final:", test3.run())
}

这将输出:

Test1 final: [0 1] 
Test2 final: [0 1] 
Test3 step 1 [0]
Test3 step 2 [0 1]
Test3 final: []

你需要帮助吗?

英文:

I'm trying to append a value to a golang slice, the code works if it's called in the first method, but if this method calls another method, the code seems to fail.

Examples (Test3 is what I was originally trying to do):

package main
import (
&quot;fmt&quot;
)
// This works
type Test1 struct {
all []int
}
func (c Test1) run() []int {
for i := 0; i &lt; 2; i++ {
c.all = append(c.all, i)
}
return c.all
}
// This works
var gloabl_all []int
type Test2 struct {}
func (c Test2) run() []int {
c.combo()
return gloabl_all
}
func (c Test2) combo() {
for i := 0; i &lt; 2; i++ {
gloabl_all = append(gloabl_all, i)
}
}
// This doesn&#39;t
type Test3 struct {
all []int
}
func (c Test3) run() []int {
c.combo()
return c.all
}
func (c Test3) combo() {
for i := 0; i &lt; 2; i++ {
c.all = append(c.all, i)
fmt.Println(&quot;Test3 step&quot;, i + 1, c.all)
}
}
func main() {
test1 := &amp;Test1{}
fmt.Println(&quot;Test1 final:&quot;, test1.run(), &quot;\n&quot;)
test2 := &amp;Test2{}
fmt.Println(&quot;Test2 final:&quot;, test2.run(), &quot;\n&quot;)
test3 := &amp;Test3{}
fmt.Println(&quot;Test3 final:&quot;, test3.run())
}

This outputs:

Test1 final: [0 1] 
Test2 final: [0 1] 
Test3 step 1 [0]
Test3 step 2 [0 1]
Test3 final: []

Playground copy: https://play.golang.org/p/upEXINUvNu

Any help would be greatly appreciated!

答案1

得分: 13

Go语言中的一切都是按值传递的。传递的值会被复制。

Test3.combo() 使用值(非指针)接收器:

func (c Test3) run() []int {
c.combo()
return c.all
}
func (c Test3) combo() {
for i := 0; i < 2; i++ {
c.all = append(c.all, i)
fmt.Println("Test3 step", i + 1, c.all)
}
}

这意味着当从Test3.run()中调用Test3.combo()时,会对c(类型为Test3)进行复制。combo()方法在复制上操作。它正确地将2个数字附加到Test3.all,但当该方法返回时,复制被丢弃。

因此,当Test3.run()返回c.all时,它返回一个空(nil)切片,因为Test3.combo()附加的切片是复制的字段,而该复制已被丢弃。

解决方法:简单地使用指针接收器:

func (c *Test3) combo() {
for i := 0; i < 2; i++ {
c.all = append(c.all, i)
fmt.Println("Test3 step", i + 1, c.all)
}
}

输出(在Go Playground上尝试):

Test1 final: [0 1] 
Test2 final: [0 1] 
Test3 step 1 [0]
Test3 step 2 [0 1]
Test3 final: [0 1]

注意接收器中的星号*func (c *Test3) combo()。通过添加它,您使接收器成为一个指针,因此当调用combo()时,它只接收指向类型为Test3的值的指针,并且它将修改指向的值,即Test3.run()拥有的值,因此当combo()返回时,更改不会丢失。

英文:

Everything in Go is passed by value. And a copy is made of the passed value.

Test3.combo() has value (non-pointer) receiver:

func (c Test3) run() []int {
c.combo()
return c.all
}
func (c Test3) combo() {
for i := 0; i &lt; 2; i++ {
c.all = append(c.all, i)
fmt.Println(&quot;Test3 step&quot;, i + 1, c.all)
}
}

This means when Test3.combo() is called from Test3.run() like c.combo(), a copy is made of c (which is of type Test3). The combo() method operates on a copy. It properly appends 2 numbers to Test3.all, but when this method returns, the copy is discarded.

So when Test3.run() returns c.all, it returns an empty (nil) slice, because the slice to which Test3.combo() appended, was a field of a copy, and which has been discarded.

Solution: simply use a pointer receiver:

func (c *Test3) combo() {
for i := 0; i &lt; 2; i++ {
c.all = append(c.all, i)
fmt.Println(&quot;Test3 step&quot;, i + 1, c.all)
}
}

Output (try it on the Go Playground):

Test1 final: [0 1] 
Test2 final: [0 1] 
Test3 step 1 [0]
Test3 step 2 [0 1]
Test3 final: [0 1]

Note the star * in the receiver: func (c *Test3) combo(). By adding it, you make the receiver a pointer, and so when combo() is called, it only receives a pointer to a value of type Test3, and it will modify the pointed value, the value that Test3.run() has, so when combo() returns, the changes are not lost.

huangapple
  • 本文由 发表于 2017年2月16日 20:39:05
  • 转载请务必保留本文链接:https://go.coder-hub.com/42274235.html
匿名

发表评论

匿名网友

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

确定