Home > Software design >  Golang: Can channels be closed in a deferred function?
Golang: Can channels be closed in a deferred function?

Time:02-13

I was working on some Go code and wanted to close a channel at the end of it. However, I wanted to test if it'd actually close the channel if I use an anonymous function, passing the channel as an argument and use the function with the defer keyword.

channel := make(chan string, 2) 

defer func (channel chan string) {
        close(channel)
        value, ok := <-channel
        fmt.Println("Value:", value, "Open channel?", ok)
    }(channel)

To my understanding everything in Go is passed by value and not by reference, and make's documentation says that it returns a channel when used to spawn a channel, not a pointer to a channel. Does this imply that if I pass the channel as argument to a function and close the channel within that function, the channel outside the function would remain open? If so, I'm guessing I'd need to pass the channel as a pointer to the function to achieve that behavior?

I'm new to Go, so I'd appreciate if you can explain me the "why" rather than "Oh, just swap this chunk for this other chunk of code" just so I can better understand how it's actually working:)

Thanks!

CodePudding user response:

Channels behaves like slice, map, strings or functions, as in they actually store a wrapped pointer to the underlying data. They behave like opaque pointers.

So passing a channel is copying it by value, but just like a slice, it's "value" is literally just a reference to the actual low-level channel data structure. So there is no need to explicitly pass a channel pointer, since that would be like passing a pointer of a pointer.

To answer your questions: Yes it can and yes it's safe to pass channels by value because they are "pointers" in and of themselves.


You can also verify it with a slightly modified version of your code:

package main

import "fmt"

func main() {
    channel := make(chan string, 2)

    // channel is open here.

    // close the channel by passing it into a function
    // by value and closing it within the function.
    func(channel chan string) {
        close(channel)
    }(channel)

    // is the channel closed?
    value, ok := <-channel
    fmt.Println("Value:", value, "Open channel?", ok)
}

Output (go version go1.16 darwin/amd64):

Value:  Open channel? false

CodePudding user response:

The channel value resulting from the make(chan string, 2) is actually an value of *hchan type. See the Go implementation's runtime.makechan function.

If channels worked like you theorized, then the following would print two different memory addresses. But the following actually prints the same memory address, consistent with what we'd expect based on the underlying type being *hchan.

package main

import (
    "fmt"
)

func f(channel chan string) {
    fmt.Printf("%p\n", channel)
}

func main() {
    channel := make(chan string, 2)
    fmt.Printf("%p\n", channel)
    f(channel)
}

Similarly, if we define the Int type as shown below, even though the argument to f is still passed by value technically, but the value is a pointer.

type Int *int

func f(i Int) {
    // do something with i
}
  • Related