为什么一个 `[]struct` 切片的行为与 `[]builtin` 不同?

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

Why a slice []struct doesn't behave same as []builtin?

问题

这里的切片是对底层数组的引用。这在内置/原始类型上是有意义的,并且似乎也能正常工作,但为什么在结构体上不起作用呢?我猜想,即使我更新了结构体字段,引用/地址仍然是相同的。

package main

import "fmt"

type My struct {
    Name string
}

func main() {
    x := []int{1}
    update2(x)
    fmt.Println(x[0])
    update(x)
    fmt.Println(x[0])
    my := My{Name: ""}
    update3([]My{my})
    // 为什么 my[0].Name 不是 "many"?
    fmt.Println(my)
}

func update(x []int) {
    x[0] = 999
    return
}
func update2(x []int) {
    x[0] = 1000
    return
}
func update3(x []My) {
    x[0].Name = "many"
    return
}

为了澄清:我知道我可以在这两种情况下使用指针。我只是好奇为什么结构体没有被更新(不像整数那样)。

英文:

The slices are references to the underlying array. This makes sense and seems to work on builtin/primitive types but why is not working on structs? I assume that even if I update a struct field the reference/address is still the same.

package main

import "fmt"

type My struct {
	Name string
}

func main() {
	x := []int{1}
	update2(x)
	fmt.Println(x[0])
	update(x)
	fmt.Println(x[0])
	my := My{Name: ""}
	update3([]My{my})
	// Why my[0].Name is not "many" ?
	fmt.Println(my)
}

func update(x []int) {
	x[0] = 999
	return
}
func update2(x []int) {
	x[0] = 1000
	return
}
func update3(x []My) {
	x[0].Name = "many"
	return
}

To clarify: I'm aware that I could use pointers for both cases. I'm only intrigued why the struct is not updated (unlike the int).

答案1

得分: 2

当调用update3时,你所做的是传递一个包含值副本的新数组,并立即丢弃该数组。这与你对原始类型的处理方式不同,因为你保留了数组。

这里有两种方法。

1) 使用指针数组而不是值数组:

你可以这样定义update3

func update3(x []*My) {
    x[0].Name = "many"
    return
}

并使用以下方式调用它:

update3([]*My{&my})

2) 在数组中写入(与处理原始类型的方式相同)

arr := make([]My,1)
arr[0] = My{Name: ""}
update3(arr)
英文:

What you do when calling update3 is you pass a new array, containing copies of the value, and you immediately discard the array. This is different from what you do with the primitive, as you keep the array.

There are two approaches here.

1) use an array of pointers instead of an array of values:

You could define update3 like this:

func update3(x []*My) {
    x[0].Name = "many"
    return
}

and call it using

update3([]*My{&my})

2) write in the array (in the same way you deal with the primitive)

arr := make([]My,1)
arr[0] = My{Name: ""}
update3(arr)

答案2

得分: 0

Go FAQ:

> 与C语言系列中的所有语言一样,Go中的所有内容都是按值传递的。也就是说,函数始终会得到被传递的东西的副本,就好像有一个赋值语句将该值分配给参数一样。例如,将int值传递给函数会创建int的副本,将指针值传递给函数会创建指针的副本,但不会创建指针所指向的数据的副本。(有关此对方法接收器的影响的讨论,请参见下一节。)

> Map和slice值的行为类似于指针:它们是包含指向底层map或slice数据的指针的描述符。复制map或slice值不会复制它们所指向的数据。

因此,当你传递my时,你传递的是你的结构体的副本,调用代码不会看到对该副本所做的任何更改。

要使函数更改结构体中的数据,你必须传递一个指向该结构体的指针。

英文:

From the GO FAQ:

> As in all languages in the C family, everything in Go is passed by
> value. That is, a function always gets a copy of the thing being
> passed, as if there were an assignment statement assigning the value
> to the parameter. For instance, passing an int value to a function
> makes a copy of the int, and passing a pointer value makes a copy of
> the pointer, but not the data it points to. (See the next section for
> a discussion of how this affects method receivers.)

> Map and slice values behave like pointers: they are descriptors that
> contain pointers to the underlying map or slice data. Copying a map or
> slice value doesn't copy the data it points to.

Thus when you pass my you are passing a copy of your struct and the calling code won't see any changes made to that copy.

To have the function change the data in teh struct you have to pass a pointer to the struct.

答案3

得分: 0

你的第三个测试与前两个不同。看一下这个(Playground)。在这种情况下,你不需要使用指针,因为你并没有修改切片本身,而是修改了底层数组的一个元素。如果你想修改切片,比如追加一个新元素,你需要使用指针将切片按引用传递。注意,我修改了打印语句以显示类型和值。

英文:

Your third test is not the same as the first two. Look at this (Playground). In this case, you do not need to use pointers as you are not modifying the slice itself. You are modifying an element of the underlying array. If you wanted to modify the slice, by for instance, appending a new element, you would need to use a pointer to pass the slice by reference. Notice that I changed the prints to display the type as well as the value.

huangapple
  • 本文由 发表于 2015年5月24日 17:23:42
  • 转载请务必保留本文链接:https://go.coder-hub.com/30421921.html
匿名

发表评论

匿名网友

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

确定