Channels and variadic parameters compiler problem

Hi,

I’m new to golang, I was going through the goroutines subject and I would like to create variadic merger of channels. Here is a on-line compiler A Tour of Go and here is the code I wrote:


package main

import "fmt"


func generate(v int) <- chan int {
	ch := make(chan int, 1)
	go func(){
		for i := v; ; i++ {
			ch <- i
		}
	}()
	
	return ch
}

func merge(v ... <-chan int ) <- chan int {
	ch := make( chan int, 1 )
	for  z := range v {
		go func() { for { ch <- <-z } }()
	}
	return ch
}

func main() {
	ch := merge( generate(1000), generate(100), generate(100000) )
	
	for i := 0; i < 10; i++ {
	fmt.Println(<- ch)
	}

}



As a response I get

# example
./prog.go:20:27: invalid operation: <-z (receive from non-chan type int)

Why it is? Shouldn’t go recognise that I’m going through the channels in a for loop?

Thanks!

Well I thought that this is the solution as I found someone doing the variadic channels iteration



package main

import "fmt"
import "time"
import "math/rand"


func generate(v int) <- chan int {
	ch := make(chan int, 1)
	go func(){
		for i := v; ; i++ {
			ch <- i
			 time.Sleep(time.Duration(rand.Intn(100))*time.Millisecond)
		}
	}()
	
	return ch
}

func merge(v ... <-chan int ) <- chan int {
	ch := make( chan int, 1 )
	for  i, z := range v {
		fmt.Printf("Merging %v %s\n", i, z)
		go func() { for { ch <- <-z } }()
	}
	return ch
}

func main() {
	ch := merge( generate(1000), generate(100), generate(100000) )
	
	for i := 0; i < 10; i++ {
	fmt.Println(<- ch)
	}

}


But then found that only last channel is taking a lead, first two channels are ignored.

Hi @aleksanderacai ,

I see a classic caveat (and I guess virtually everyone stumbles upon this one):

The goroutine inside merge is a closure, hence the outer variables are accessible to the goroutine.

However, the goroutines spawned inside the loop may not have a chance to run until the loop has finished.

And when the goroutines finally can start running, they all grab the same value of z, which is the final value from the range over v.

→ Always pass loop data to goroutine closures via parameters. Then every goroutine receives the data from the respective loop iteration.

		go func(c <-chan int) { for { ch <- <-c } }(z)

Playground: The Go Playground

Thanks! It worked! So my suggestion is to put that example in tutorial of Golang.

1 Like

Perfect! Happy to hear that.

I don’t know the maintainers of the official Go tutorials, but I wrote a short article about the problem for future reference. :slight_smile:

This is the classic goroutine closures inside a loop issue. It is already well known and part of the documentation, no need to add anything more.

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.