Search code examples
goconcurrencychannelgoroutine

Calling defer in the for loop - Is there a better way to defer reading response from channels?


I am trying to call a function that returns a channel in a loop. The channel is then appended onto a slice channelSlice. At the end, the channel slice is iterated and the response from each channel is printed. While I do this, my IDE shows a warning:

Possible resource leak, 'defer' is called in the 'for' loop 

As you could see, I call close(channelSlice[i]) inside the second for loop. Is this not recommended? Also, how could this lead to a resource leak? Is there a better way to handle close or slice of channels?

package main


import (
    "fmt"
    "math/rand"
)

func t() chan int {
    c := make(chan int)
    go func() {
        c <- rand.Intn(100)
    }()

    return c
}

func main() {
    channelSlice := make([]chan int, 0)
    for i := 0; i<100; i++ {
        // Keep making concurrent calls
        // Will read responses from the channel later
        channelSlice = append(channelSlice, t())
    }

    for i := 0; i<100; i++ {
        defer close(channelSlice[i]) // Warning thrown by IDE
        fmt.Println(<-channelSlice[i])
    }
}

Solution

  • As pointed by @mkopriva,

    Deferred calls are executed when the surrounding function exits, they are not called when the surrounding non-function block exits. Enclose the loop's body in a closure.

    Here is what I did:

        for i := 0; i<100; i++ {
            func() {
                defer close(channelSlice[i])
                fmt.Println(<-channelSlice[i])
            }()
        }
    

    As you could see, I wrapped the defer statement in a IIFE (Immediately Invoked Function Expression). Making it a closure would be fine as well.

    This ensures, that the channel will now be closed and there will not be a memory leak.