dsb12300 2019-09-13 09:01
浏览 62
已采纳

对级联保存映射的结构的空括号初始化

I have the following data structure. It a chain of a struct that each has map[string]T. Basically I'm serializing a complex yaml file to a data structure. I have a two version that does work but one doesn't and it not clear for me why? Based on my understand Go compiler is very smart so it supposes to figure out where object needs to be allocated.

Please consider a code below.

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 ? 

}

if a caller does something like

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

It doesn't work -- First call does create a map and puts a value but the second call doesn't work. The key is never added to the second map.

In a version that does work, I created a constructor function that does(same semantic in each method). For example, version that does work below. (I use here the same semantic I usually do with C++ and other languages)

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()
}

I guess when you get used to one language compiler magic makes life harder :)

  • 写回答

1条回答 默认 最新

  • drl959975 2019-09-13 09:20
    关注

    Right, the problem is that your AddXXX funcs are returning copies/values, where they should be returning pionters if you want your code to work properly. You also have to change the return type of the AddGroup func to Groups (and possibly rename the func to reflect the actual type name). I'd also avoid using make when a literal is shorter, and more idiomatic.

    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
    }
    

    The problem you have has got nothing to do with where an object is allocated, but everything with what object you're working with. If you call AddCluster, and aren't returning a pointer, then the call on the returned Cluster object (AddGroup), is going to add a group to a completely different object than the one in the map you've created. What you're doing is the C/C++ equivalent of something along these lines:

    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
    ", i, cl.foo);
       return 0;
    }
    

    Quite clearly, you're incrementing a copy of the int val assigned to cl.foo. You're not updating both.


    Update (clarification):

    A pointer var can be allocated on the stack or heap, that's up to the runtime to decide. Because value vs pointer returns are semantically/functionally different, however. Should the compiler make assumptions based on whatever reason, we'd all be in a world of hurt. Consider the following code in a program that heavily uses concurrency:

    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
    }
    

    If b, the return value, would be "secretly" returned as a pointer, but another routine is also updating that same value, then you'd have a race condition. What's more, there are plenty of legitimate cases where you really don't want a pointer:

    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, in this kind of GetBar function, I'd probably return a pointer, but return it like so:

    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
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 如何在node.js中或者java中给wav格式的音频编码成sil格式呢
  • ¥15 不小心不正规的开发公司导致不给我们y码,
  • ¥15 我的代码无法在vc++中运行呀,错误很多
  • ¥50 求一个win系统下运行的可自动抓取arm64架构deb安装包和其依赖包的软件。
  • ¥60 fail to initialize keyboard hotkeys through kernel.0000000000
  • ¥30 ppOCRLabel导出识别结果失败
  • ¥15 Centos7 / PETGEM
  • ¥15 csmar数据进行spss描述性统计分析
  • ¥15 各位请问平行检验趋势图这样要怎么调整?说标准差差异太大了
  • ¥15 delphi webbrowser组件网页下拉菜单自动选择问题