'Static global variables in Go

I am new in Go language and I need some help. I have declared a global variable but the problem is that it does not keep its value. It would be solved with a static variable but no such variables exist in Go. How should I solve this?

gID is a global variable. This function is called twice. On the first call if code is executed. On the second, else code is executed. I want the second struct Learner instance, to get first Learner's instance ID.

I have to mention that NewLearner is called twice from two different files from different packages.

func NewLearner(name string, peerURLs types.URLs, clusterName string, now *time.Time) *Learner {

    l := &Learner{
        RaftAttributes: RaftAttributes{PeerURLs: peerURLs.StringSlice()},
        Attributes:     Attributes{Name: name},
    }

    var b []byte
    sort.Strings(l.PeerURLs)
    for _, p := range l.PeerURLs {
        b = append(b, []byte(p)...)
    }

    b = append(b, []byte(clusterName)...)
    if now != nil {
        b = append(b, []byte(fmt.Sprintf("%d", now.Unix()))...)
        hash := sha1.Sum(b)
        l.ID = types.ID(binary.BigEndian.Uint64(hash[:8]))
        gID=l.ID
        return l
    }  else {
        l.ID = gID
        return l    
    }

}
go


Solution 1:[1]

The fact that the package variable doesn't keep its value might be caused by concurrency. I would solve your problem that way. Use a package variable with a mutex around in case the function is called concurrently.

import "sync"

var(
    gID  uint64
    muID sync.Mutex
)

func myfunc(param bool) {
    var id uint64
    if param {
        id = newID()

        muID.Lock()
        gID = id
        muID.Unlock()
    } else {
        muID.Lock()
        id = gID
        muID.Unlock()
    }
}

Solution 2:[2]

How about using a channel? You can have channel (global in your context) of uint64.

idChan := make(chan uint64)

And then inside your if condition you can put the gID in the idChan and read the same from your else condition.. something like this:

    if now != nil {
        b = append(b, []byte(fmt.Sprintf("%d", now.Unix()))...)
        hash := sha1.Sum(b)
        l.ID = types.ID(binary.BigEndian.Uint64(hash[:8]))
        idChan <- l.ID
        return l
    }  else {
        l.ID <- idChan
        return l    
    }

Do remember to close your channel appropriately though if you dont want to use it anywhere else anymore.

Although, i must advise that such stuffs are usually better and more safely handled by goroutines with waitgroups probably since you actually want to share a value. In your case something like maybe a goroutine that does if part and another that does the else part and then share this channel amongst those two goroutines and wait on the wait groups for code that depends on these goroutines completion. That really is what concurrency is.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Dolanor
Solution 2 Nik