首页 > 解决方案 > 通过指针的指针更新结构不起作用

问题描述

好的,对于一些简化的设置,在这个例子中我们有三个 structs ,comp看起来有点像这样:aggcache

type comp struct {
    id  uint64
    val float64
}

type agg struct {
    id   uint64
    vals []*comp
}

type cache struct {
    compMtx sync.RWMutex
    comps map[uint64]*comp
    aggMtx sync.RWMutex
    aggs  map[uint64]*agg
}

其中cache具有以下添加新comp值的功能,在更新的情况下似乎不起作用:

func (c *cache) NewComp(cpNew *comp) {
    compMtx.Lock()
    defer compMtx.Unlock()
    cpOld, ok := c.comps[cpNew.id]
    if ok { // update
        addr := &cpOld // this is of type **comp
        *addr = cpNew
    } else { // new value
        c.comps[cpNew.id] = cpNew
    }
}

这种方法背后的思想是,通过改变指针指向的位置,我们可以确保comp指针agg.vals始终指向给定comp对象的最新迭代。

至于这种方法背后的原因,遍历整个agg.vals数组以查找给定comp对象的索引将 a)由于数组(相当大)的大小而在计算上是昂贵的,并且 b)需要agg通过一个锁定internalsync.Mutex在搜索期间阻止不同的线程访问对象,这两者都是不可取的。此外假设制作agg.value地图以便于轻松更新是不可能的。

但是由于NewComp上面的实现不起作用,我的问题是上面的函数是否有任何明显的错误,或者我在这里的思考中是否犯了一些基本错误?


由于这可能会有所帮助,这里还有一个示例,其中通过指针的指针进行的更新按预期工作:

type wrks struct {
    id   uint64
    val1 *comp
    val2 *comp
}

func compute() *comp {...}

func updateComp(c **comp) {
    *c = compute()
}

func processObj(o *obj) {
    updateComp(&o.val1)
    updateComp(&o.val2)
}

我看不出两者之间的根本区别,但此时我可能已经盯着这个看太久了。

标签: dictionarygopointersstruct

解决方案


您将指针存储在映射中,因此当您从中获取指针时,只需修改指向的值,分配给指向的值:

cpOld, ok := c.comps[cpNew.id]
if ok { // update
    *cpOld = *cpNew
} else { // new value
    c.comps[cpNew.id] = cpNew
}

请参阅Go Playground上的一个简化示例,该示例显示了此方法。

您的原始代码不起作用,因为cpOld它是一个指针,但它是存储在地图中的指针的副本。如果您修改此cpOld变量的值,则不会影响地图中存储的值。您不能更改地图中的值,只能在其中重新分配/存储新值。

您应该记住一件事:cpNew传递给的指针NewComp()在更新后将不会“有用”,因为我们没有使用指针,我们只是使用指向的值来更新映射已经指向的值(a值存储在地图中)。如果您希望指针继续指向相同的值,您别无选择,只能将该指针存储在映射中,就像它是新的一样:

cpOld := c.comps[cpNew.id] = cpNew

请参阅相关问题:如何在 Go 中更新地图值


推荐阅读