Channels vs. "for loop" prime number generator
yes it can be improved.

first, Golang is supposed to be big on concurrency. Your version is not concurrent. It will probably run on just one core.

Channels are supposed to give us concurrency for free. Here, we have a cascade of filtering channels, and while a number makes its way through a later channel, another, bigger number can well enter the earlier channel, concurrently. On multi-core, I'd expect to see high CPU utilization for this code.

The channel version can be drastically improved. Filtering by 7, say, should only be done for numbers from 49 up. There's no need to check 28 by 7&mdash;it'll be caught earlier, by a lower prime factor, 2. So the filtering channels should only be started up at primes' squares. This will radically reduce the number of channels working largely for nothing right now. The time complexity should improve from about quadratic to about n1.5, give or take.

I call it the postponement technique. I described it in this SO answer as well as many others. I first thought it up when I worked through the SICP book many years ago and tried re-writing its primes code in Haskell for fun. I thought then it must be widely known... but it turns out, it wasn't.

And of course, there's no need to enumerate any even number above 2. Here's the code:

func Sieve(out chan&lt;- int) {   // 2,000 primes:
  out &lt;- 2                     //  was: 1.32s now: 0.06s
  out &lt;- 3
  q := 9
  ps := make(chan int)
  go Sieve(ps)                 // separate primes supply
  p := &lt;-ps   // p=2, ps@3
  p = &lt;-ps    // p=3, ps@5
  nums := make(chan int)
  go NumsFromBy(5,2,nums)
  for i := 0; ; i++ {
    n := &lt;-nums
    if n &lt; q {
        out &lt;- n   // n is prime
    } else {
        ch1 := make(chan int)
        go Filter(nums, ch1, p)  // p=3
        nums = ch1
        p = &lt;-ps  // 5
        q = p*p   // 25 !
    }
  }
}