为啥没有附加结构的切片字段?

Posted

技术标签:

【中文标题】为啥没有附加结构的切片字段?【英文标题】:Why is the slice field of a struct not appended to?为什么没有附加结构的切片字段? 【发布时间】:2021-10-23 09:51:37 【问题描述】:

以下代码的输出让我吃惊:

package main

import (
    "fmt"
)

type Thing struct 
  mappings  map[string]int
  orderings []string


func NewThing() Thing 
  t := Thing
  t.mappings = make(map[string]int)
  return t


func (t Thing) Add(s string) 
  t.mappings[s] = 1
  t.orderings = append(t.orderings, s)


func main() 
  t := NewThing()
  t.Add("foo")

  if len(t.mappings) == len(t.orderings) 
    fmt.Printf("Equal lengths: %v versus %v", t.mappings, t.orderings)
   else 
    fmt.Printf("Unequal lengths: %v versus %v", t.mappings, t.orderings)
  

在操场 (https://play.golang.org/p/Ph67tHOt2Z_I) 上运行时,输出如下:

Unequal lengths: map[foo:1] versus []

我相信我正确地处理了切片;据我了解,它在 NewThing() 中初始化为 nil,并在 Add() 中附加(确保从 append 返回的值仅分配给它的第一个参数)。

我是否遗漏了一些非常明显的东西?

我查看了以下资源以获得解释:

https://gobyexample.com/slices - 仅使用切片文字(即不是结构字段)或具有设定容量的切片,我不知道 t.orderings 的最终大小。我的理解是 append 应该自动执行扩展和分配。

https://go.dev/blog/slices-intro - 同样,所有演示都使用切片文字。如果将字段移出结构,则事情会按预期工作。这种行为在结构中只发生一次。

https://yourbasic.org/golang/gotcha-append/ - 虽然它确实描述了 append 无法按预期工作的行为,但解释涉及在 slice 有足够的容量容纳新元素时追加重用内存,当尝试将相同的数组追加到两个不同的数组时会导致意外的行为副本。在我的例子中,没有像本文中那样重新分配切片操作,不鼓励这样做 (some_var = append(some_other_var, elem))。

我查看了以下问题以获得灵感:

Go - append to slice in struct: 这个问题的解决方案是将 append 的结果分配回字段,我已经这样做了。

Correct way to initialize empty slice: 解释是切片不必初始化,可以保留为 nil 并“附加到分配”,所以我相信我可以不初始化 Thing.orderings。

【问题讨论】:

append(some_other_var, elem) 返回一个新值。鉴于您的 t Thing 是一个临时副本 - 该值丢失了。如果你希望你的 t Thing 是可变的,你应该让它成为一个指针:t *Thing func (t Thing) Add(s string) Add 方法时,它都会创建一个Thing 的临时副本并将其分配给t。函数返回后t 丢失”。您如何创建实例并不重要。方法的签名是什么很重要。 “那为什么不会影响地图” --- 因为你没有重新分配地图而是改变它。 @wherezyurheadat 因为如前所述,初始化的方式很重要,重要的是 Add 方法的声明。 @wherezyurheadat “并且不想” --- 那么你将无法改变结构的字段。 “再一次,地图不受任何影响” ---因为您没有重新分配地图。如果您重新分配地图 - 您将看到相同的“问题”。 地图改变是因为你改变了地图,你没有重新分配它。 【参考方案1】:

如果你不想使用指针,你可以为 Thing struct 声明一个全局变量,并从 add 函数中为其分配 t 的值。这是相同逻辑的代码:

package main

import (
    "fmt"
)

var thing Thing

type Thing struct 
    mappings  map[string]int
    orderings []string


func NewThing() Thing 
    t := Thing
    t.mappings = make(map[string]int)
    return t


func (t Thing) Add(s string) 
    t.mappings[s] = 1
    t.orderings = append(t.orderings, s)

    thing = t



func main() 
    t := NewThing()
    t.Add("foo")

    if len(thing.mappings) == len(thing.orderings) 
        fmt.Printf("Equal lengths: %v versus %v", thing.mappings, thing.orderings)
     else 
        fmt.Printf("Unequal lengths: %v versus %v", thing.mappings, thing.orderings)
    

输出:

Equal lengths: map[foo:1] versus [foo]

【讨论】:

以上是关于为啥没有附加结构的切片字段?的主要内容,如果未能解决你的问题,请参考以下文章

为啥带有附加字段“指定”的字段始终为空?

附加到作为空接口传递的 golang 切片

Go Pointers - 通过指针将值附加到切片

为啥这个 html 附加结构与数组结构不同

为啥片段着色器没有附加?

如何在json数组结构中添加附加字段