【发布时间】: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 “并且不想” --- 那么你将无法改变结构的字段。 “再一次,地图不受任何影响” ---因为您没有重新分配地图。如果您重新分配地图 - 您将看到相同的“问题”。
-
地图改变是因为你改变了地图,你没有重新分配它。
标签: go