【问题标题】:Why a slice []struct doesn't behave same as []builtin?为什么 slice []struct 的行为与 []builtin 不同?
【发布时间】:2015-05-24 09:23:42
【问题描述】:

切片是对底层数组的引用。这是有道理的,并且似乎适用于内置/原始类型,但为什么不适用于结构?我假设即使我更新了一个结构字段,引用/地址仍然是相同的。

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
}

澄清一下:我知道我可以在这两种情况下使用指针。我只是好奇为什么不更新结构(与 int 不同)。

【问题讨论】:

  • 出于同样的原因 z := 0; x := []int{z}; update2(x); fmt.Println(z) 不会打印 1000。您已将my 复制到一个数组中,修改数组元素(复制一个)并打印出原始的(未修改)。
  • 有道理......现在我看到了不同......

标签: go reference


【解决方案1】:

在调用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)

【讨论】:

  • 怎么样?为什么 update(x []int) 的行为不一样? (即复制值 int)是什么让结构成为特例?
  • @Theuserwithnohat 您的带有原语的代码类似于我提到的第二种方法
【解决方案2】:

来自GO FAQ:

与 C 家族中的所有语言一样,Go 中的所有内容都是通过 价值。也就是说,一个函数总是会得到一个正在存在的东西的副本 通过,好像有一个赋值语句赋值 到参数。例如,将 int 值传递给函数 复制 int,并传递指针值复制 指针,但不是它指向的数据。 (请参阅下一节 讨论这如何影响方法接收器。)

Map 和 slice 值的行为类似于指针:它们是描述符 包含指向底层映射或切片数据的指针。复制地图或 切片值不会复制它指向的数据。

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

要让函数更改结构中的数据,您必须将指针传递给结构。

【讨论】:

    【解决方案3】:

    您的第三个测试与前两个不同。看看这个(Playground)。在这种情况下,您不需要使用指针,因为您没有修改切片本身。您正在修改底层数组的元素。如果您想修改切片,例如添加一个新元素,您需要使用指针通过引用传递切片。请注意,我更改了打印以显示类型和值。

    【讨论】:

      猜你喜欢
      • 2014-01-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-15
      相关资源
      最近更新 更多