gpt4 book ai didi

pointers - 包含级联映射的结构的空括号初始化

转载 作者:数据小太阳 更新时间:2023-10-29 03:30:45 27 4
gpt4 key购买 nike

我有以下数据结构。它是一个结构链,每个结构都有 map[string]T。基本上我将一个复杂的 yaml 文件序列化为一个数据结构。我有两个版本可以工作,但一个不能,我不清楚为什么?根据我的理解,Go 编译器非常聪明,所以它应该找出需要分配对象的位置。

请考虑下面的代码。

type UserData struct {
Username string
Password string
}
type Groups struct {
users map[string] UserData
}
type Cluster struct {
Group map[string] Groups
}
type Director struct {
Cluster map[string]Cluster
}

//... I removed other add function.. Same semantic each add X function
// check if a map is nil call make(map[string]T) add a key)
// add a key with struct (where struct hold nil map)
func (c *Cluster) AddGroup(groupName string) Group {
if c.Group == nil {
c.Group = make(map[string]Groups)
}

c.Group[groupName] = Groups {}
group, _ := c.Group[groupName]
return group
}

func (p *Director) AddCluster(clusterName string) Cluster {
if p.Cluster == nil {
p.Cluster = make(map[string]Cluster)
}

p.Cluster[clusterName] = Cluster{}
cluster, _ := p.Cluster[clusterName]
return cluster
// does a compiler here allocate an object on the
// stack for Cluster {} and it goes out of a scope ?

}

如果来电者做了类似的事情

var p = Director{}
cluster := p.AddCluster("Test Cluster")
cluster.AddGroup("Test Group")

它不起作用——第一次调用确实创建了一个映射并放置了一个值,但第二次调用不起作用。 key 永远不会添加到第二张 map 。

在一个确实有效的版本中,我创建了一个构造函数(每个方法中的语义相同)。例如,下面确实有效的版本。 (我在这里使用与 C++ 和其他语言相同的语义)

func NewCluster() *Cluster {
var cluster = Cluster{}
cluster.Group = make(map[string]Groups)

return &cluster
}

func (p *Director) AddCluster(clusterName string) {
if p.Cluster == nil {
p.Cluster = make(map[string]Cluster)
}

p.Cluster[clusterName] = *NewCluster()
}

我想当你习惯了一种语言时,编译器的魔力会让生活变得更加艰难:)

最佳答案

是的,问题是您的 AddXXX 函数正在返回副本/值,如果您希望代码正常工作,它们应该返回指针。您还必须将 AddGroup 函数的返回类型更改为 Groups(并且可能重命名该函数以反射(reflect)实际类型名称)。当文字更短且更惯用时,我也会避免使用 make

func (c *Cluster) AddGroup(groupName string) *Groups {
if c.Group == nil {
c.Group = map[string]*Groups{} // change type to use pointers
}

group := &Groups{} // create pointer
c.Group[groupName] = group // assign
return group // return
}

func (p *Director) AddCluster(clusterName string) *Cluster {
if p.Cluster == nil {
p.Cluster = map[string]*Cluster{} // change type
}

cluster := &Cluster{} // create ptr var
p.Cluster[clusterName] = cluster
return cluster
}

您遇到的问题与分配对象的位置无关,而是与您正在使用的对象有关。如果您调用 AddCluster,并且没有返回一个指针,那么调用返回的 Cluster 对象(AddGroup), 将向与您创建的 map 中的对象完全不同的对象添加一个组。您正在做的是 C/C++ 等同于以下内容的内容:

typedef _cluster_t struct {
int foo;
} cluster;

// and some calls like this:

int add_foo(cluster *c) {
c->foo = 123;
return c->foo;
}

int main ( void ) {
cluster cl;
int i = add_foo(&cl);
i++;
printf("I expect %d == %d\n", i, cl.foo);
return 0;
}

很明显,您正在递增分配给 cl.foo 的 int val 的副本。你没有更新两者。


更新(澄清):

指针变量可以分配在堆栈或堆上,这由运行时决定。但是,因为值与指针返回在语义/功能上是不同的。如果编译器基于任何原因做出假设,我们都会受到伤害。在大量使用并发的程序中考虑以下代码:

type Foo struct {
SomeData map[string]Bar
}

func (f *Foo) AddBar(name string) Bar {
if f.SomeData == nil {
f.SomeData = map[string]Bar{} // this already is not thread-safe!
}
b := Bar{}
f.SomeData[name] = b
return b
}

如果返回值 b“ secret 地” 作为指针返回,但另一个例程也在更新相同的值,那么您将有一个比赛条件。更重要的是,有很多合理的情况,您真的不想要指针:

type Foo struct {
mu *sync.Mutex
data map[string]*Bar
}
// GetBar - should return a SNAPSHOT of whatever Bar looks like at this moment
func (f *Foo) GetBar(name string) (Bar, error) {
f.mu.Lock()
defer f.mu.Unlock()
if b, ok := f.data[name]; ok {
// return COPY because it's a snapshot!
return *b, nil
}
return Bar{}, ErrBarNotFound
}

TBH,在这种 GetBar 函数中,我可能会返回一个指针,但返回方式如下:

if b, ok := f.data[name]; ok {
cpy := *b // create copy/snapshot
return &cpy, nil
}
// so in case of an error, I can return nil:
return nil, ErrBarNotFound

关于pointers - 包含级联映射的结构的空括号初始化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57920459/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com