如何在 Go 结构体中初始化成员

58

我是Golang的新手,因此其中的分配使我感到疯狂:

import "sync"

type SyncMap struct {
        lock *sync.RWMutex
        hm map[string]string
}
func (m *SyncMap) Put (k, v string) {
        m.lock.Lock()
        defer m.lock.Unlock()

        m.hm[k] = v, true
}

然后,我只需调用:

sm := new(SyncMap)
sm.Put("Test, "Test")

目前我遇到了一个空指针恐慌。

我通过使用另一个函数来解决问题,并在new()之后立即调用它来绕过此问题:

func (m *SyncMap) Init() {
        m.hm = make(map[string]string)
        m.lock = new(sync.RWMutex)
}

但我想知道,是否有可能去掉这个样板式的初始化过程?


m.hm[k] = v, true 这没有意义(试图将两个值分配给一个变量),当问这个问题时,这怎么可能编译通过? - Nic
3个回答

70

你只需要一个构造函数。一个常用的模式是

func NewSyncMap() *SyncMap {
    return &SyncMap{hm: make(map[string]string)}
}

如果你的结构体中有更多的字段,可以在这个构造函数中启动后台 goroutine 或者注册一个 finalizer。

func NewSyncMap() *SyncMap {
    sm := SyncMap{
        hm: make(map[string]string),
        foo: "Bar",
    }

    runtime.SetFinalizer(sm, (*SyncMap).stop)

    go sm.backend()

    return &sm
}

1
非常感谢!现在我记得,在教程中有关于构造函数的内容,但是作为Java开发人员,我认为它应该与new操作符有关,而不是New...代码约定。 - Illarion Kovalchuk
1
这个代码可以工作,但并不是最佳实践。应该将RWMutex作为一个值来包含,而非指针。它的零值是一个可直接使用的互斥锁,并且这样你就可以避免使用显式构造函数。 - kelnos
应该采用不同的名称,因为它只是一个例子。正如您所看到的,我还初始化了字段“foo”,这不是原始结构的一部分。 ;) - themue
1
编译器是否总是将NewSyncMap()指向的这种结构体分配到堆上?换句话说,为了在堆栈上实现C++对象初始化,我们必须创建一个“Init”函数来修改现有的结构体吗? - mk12

11

由于未初始化互斥锁,'Mue'方案无法使用。以下修改方法可行:

package main

import "sync"

type SyncMap struct {
        lock *sync.RWMutex
        hm map[string]string
}

func NewSyncMap() *SyncMap {
        return &SyncMap{lock: new(sync.RWMutex), hm: make(map[string]string)}
}

func (m *SyncMap) Put (k, v string) {
        m.lock.Lock()
        defer m.lock.Unlock()
        m.hm[k] = v
}

func main() {
    sm := NewSyncMap()
    sm.Put("Test", "Test")
}

http://play.golang.org/p/n-jQKWtEy5


1
非常感谢!未初始化的互斥锁会导致一个非常难以调试的微妙错误。Lock()和Unlock()调用成功,但访问并没有同步。 - Steve

5
德蒙很敏锐地指出了问题。Mue可能在想将锁作为值而不是指针的更常见模式。由于Mutex的零值是一个可用的未锁定的Mutex,因此它不需要初始化,并且将其作为值包含是常见的。进一步简化,您可以通过省略字段名称来嵌入它。然后,您的结构体将获取Mutex的方法集。请参见此工作示例:http://play.golang.org/p/faO9six-Qx。我还去掉了使用defer的部分。在某种程度上,这是偏好和编码风格的问题,但由于它确实具有一些开销,因此我倾向于在小函数中不使用它,特别是如果没有条件代码。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接