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 关于#matlab#的问题:在模糊控制器中选出线路信息,在simulink中根据线路信息生成速度时间目标曲线(初速度为20m/s,15秒后减为0的速度时间图像)我想问线路信息是什么
  • ¥15 banner广告展示设置多少时间不怎么会消耗用户价值
  • ¥16 mybatis的代理对象无法通过@Autowired装填
  • ¥15 可见光定位matlab仿真
  • ¥15 arduino 四自由度机械臂
  • ¥15 wordpress 产品图片 GIF 没法显示
  • ¥15 求三国群英传pl国战时间的修改方法
  • ¥15 matlab代码代写,需写出详细代码,代价私
  • ¥15 ROS系统搭建请教(跨境电商用途)
  • ¥15 AIC3204的示例代码有吗,想用AIC3204测量血氧,找不到相关的代码。