2010-12-21 10 views
50

Soy nuevo en Golang modo de asignación en que me hace loco:cómo inicializar miembros en Ir estructura

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 
} 

y más tarde, acabo de llamar:

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

En este momento recibo una nil puntero pánico.

He trabajado alrededor de ella utilizando otra función, y decir que es justo después de new():

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

Pero me pregunto, si es posible deshacerse de este inicialización repetitivo?

Respuesta

63

Solo necesita un constructor. Un patrón común es utilizado

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

En caso de más campos dentro de su estructura, a partir de un goroutine como back-end, o el registro de un finalizador todo lo que se podría hacer en este constructor.

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

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

    go sm.backend() 

    return &sm 
} 
+1

¡Muchas gracias! Ahora recuerdo, que había algo sobre constructor en el tutorial, pero, al ser un devloper de Java, pensé que debería estar relacionado con el nuevo operador, no con el nuevo ... código de convención –

+1

Esto funcionará, pero no es el mejor consejo. El RWMutex debe incluirse como un valor, no como un puntero. Su valor cero es un mutex listo para usar, y de esta manera puede evitar una función constructora explícita. – kelnos

+0

Debería haber tomado diferentes nombres, ya que solo es un ejemplo. Como verá, también inicializo el campo 'foo' que no es parte de la estructura original. ;) – Mue

9

La solución de 'Mue' no funciona porque el mutex no está inicializado. La siguiente modificación funciona:

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

+0

¿Por qué usar un puntero a 'sync.RWMutex'? – Danilo

+0

¡Muchas gracias! El mutex no inicializado da como resultado un error sutil que es muy difícil de depurar. Las llamadas a Bloquear() y Desbloquear() tienen éxito, pero el acceso no está sincronizado. – Steve

5

Buena captura por demonio. Probablemente, Mue estaba pensando en el patrón más común de incluir el bloqueo como un valor en lugar de un puntero. Dado que el valor cero de un Mutex es un Mutex desbloqueado listo para usar, no requiere inicialización e incluye uno ya que un valor es común. Como una simplificación adicional, puede incrustarlo omitiendo el nombre del campo. Su estructura luego adquiere el conjunto de métodos del Mutex. Vea este ejemplo de trabajo, http://play.golang.org/p/faO9six-Qx. También saqué el uso de diferir. Hasta cierto punto, es una cuestión de preferencias y estilo de codificación, pero como tiene una pequeña sobrecarga, tiendo a no usarlo en funciones pequeñas, especialmente si no hay un código condicional.

Cuestiones relacionadas