2010-12-21 15 views
50

Jestem nowy Golang więc przydział w to czyni mnie szalony:jak zainicjować członkowie Przejdź struct

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 
} 

a później, po prostu zadzwoń:

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

W tej chwili mogę dostać zero paniki paniki.

Pracowałem wokół niego za pomocą innego jedną funkcję, a nazywając ją tuż po new():

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

Ale zastanawiam się, czy to możliwe, aby pozbyć się tego boilerplate inicjalizacji?

Odpowiedz

63

Potrzebujesz tylko konstruktora. Częstym stosowany wzór jest

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

W przypadku większej ilości pól wewnątrz swojej struktury rozpoczynając goroutine jako backend lub rejestrując wszystko finalizatora można zrobić w tym konstruktora.

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

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

    go sm.backend() 

    return &sm 
} 
+1

Wielkie dzięki! Teraz pamiętam, że było coś o konstruktorze w tutorialu, ale będąc devdelerem Java, pomyślałem, że powinien on być powiązany z nowym operatorem, a nie z Nową ... konwencją kodu –

+1

To zadziała, ale nie jest najlepszą radą. RWMutex powinien być zawarty jako wartość, a nie jako wskaźnik. Jego wartość zerowa jest gotowym do użycia muteksem, w ten sposób można uniknąć jawnej funkcji konstruktora. – kelnos

+0

Powinny mieć różne nazwy, ponieważ jest to tylko przykład. Jak zobaczysz, inicjuję również pole 'foo', które nie jest częścią oryginalnej struktury. ;) – Mue

9

Rozwiązanie "Mue" nie działa, ponieważ muteks nie został zainicjowany. Poniższa modyfikacja działa:

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

Dlaczego chcesz użyć wskaźnika do 'sync.RWMutex'? – Danilo

+0

Dziękuję bardzo! Niezainicjowany muteks powoduje subtelny błąd, który jest bardzo trudny do debugowania. Wywołania funkcji Zablokuj() i Odblokuj() powiodą się, ale dostęp nie zostanie zsynchronizowany. – Steve

5

Dobry połów przez demona. Mue prawdopodobnie myślał o bardziej powszechnym schemacie włączenia zamka jako wartości niż wskaźnika. Ponieważ wartość zerowa Mutex jest gotowym do użycia odblokowanym Mutexem, nie wymaga on inicjalizacji, a jeden z nich jako wartość jest wspólny. Jako dalsze uproszczenie możesz go osadzić, pomijając nazwę pola. Twój struct następnie nabywa zestaw metod Mutex. Zobacz ten działający przykład, http://play.golang.org/p/faO9six-Qx. Również wyjąłem użycie odroczenia. Do pewnego stopnia jest to kwestia preferencji i stylu kodowania, ale ponieważ ma niewielki narzut, staram się nie używać go w małych funkcjach, szczególnie jeśli nie ma kodu warunkowego.