olu
olu

Reputation: 303

Why do we need to copy the value of u here?

I'm a beginner in Go and following an online course where this bit of code was used as an example:


func ConcurrentMutex(url string, fetcher Fetcher, f *fetchState) {
    var done sync.WaitGroup

    for _, u := range urls {
        done.Add(1)
        u2 := u
        go func() {
            defer done.Done()
            ConcurrentMutex(u2, fetcher, f)
        }()
        //go func(u string) {
        //  defer done.Done()
        //  ConcurrentMutex(u, fetcher, f)
        //}(u)
    }

    done.Wait()
    return
}



The type of u is a string, and the way I see it, we should be able to pass u to the ConcurrentMutex call in the inner function without having to copy its value to u2. However, the professor maintained that the Go memory model meant that as we are continuously changing the value of u, it can affect the different calls to ConcurrentMutex.

I'm still not entirely sure why. Shouldn't the value of u at the point the function is called be passed to the function? I would have understood if we were passing a pointer, but as we're not, it's confusing me.

Can someone please explain how Go's memory model interprets this block?

Note: The commented out inner function was the original one used in the example in the lecture video but was changed in the lecture note. It looks to me like both are equivalent, so I guess the question applies to both.

Upvotes: 0

Views: 53

Answers (1)

praveent
praveent

Reputation: 610

What you are using is closure. u2 is shared between the inner function and the function surrounding it. Which means with every iteration as u2 is modified, modified value is what is visible to inner function. Better way of writing is using the code that has been commented out. By explicitly passing a value to go routine, you ensure that go routine carries it's own copy that will not be modified by surrounding function.

What go specification says about this: Go specification

Function literals A function literal represents an anonymous function.

FunctionLit = "func" Signature FunctionBody . func(a, b int, z float64) bool { return a*b < int(z) } A function literal can be assigned to a variable or invoked directly.

f := func(x, y int) int { return x + y } func(ch chan int) { ch <- ACK }(replyChan) Function literals are closures: they may refer to variables defined in a surrounding function. Those variables are then shared between the surrounding function and the function literal, and they survive as long as they are accessible.

Hope this answers your question.

Upvotes: 2

Related Questions