Consuming all elements of a channel into a slice

phihag picture phihag · Dec 4, 2013 · Viewed 8.4k times · Source

How can I construct a slice out of all of elements consumed from a channel (like Python's list does)? I can use this helper function:

func ToSlice(c chan int) []int {
    s := make([]int, 0)
    for i := range c {
        s = append(s, i)
    }
    return s
}

but due to the lack of generics, I'll have to write that for every type, won't I? Is there a builtin function that implements this? If not, how can I avoid copying and pasting the above code for every single type I'm using?

Answer

Paul Hankin picture Paul Hankin · Dec 5, 2013

If there's only a few instances in your code where the conversion is needed, then there's absolutely nothing wrong with copying the 7 lines of code a few times (or even inlining it where it's used, which reduces it to 4 lines of code and is probably the most readable solution).

If you've really got conversions between lots and lots of types of channels and slices and want something generic, then you can do this with reflection at the cost of ugliness and lack of static typing at the callsite of ChanToSlice.

Here's complete example code for how you can use reflect to solve this problem with a demonstration of it working for an int channel.

package main

import (
    "fmt"
    "reflect"
)

// ChanToSlice reads all data from ch (which must be a chan), returning a
// slice of the data. If ch is a 'T chan' then the return value is of type
// []T inside the returned interface.
// A typical call would be sl := ChanToSlice(ch).([]int)
func ChanToSlice(ch interface{}) interface{} {
    chv := reflect.ValueOf(ch)
    slv := reflect.MakeSlice(reflect.SliceOf(reflect.TypeOf(ch).Elem()), 0, 0)
    for {
        v, ok := chv.Recv()
        if !ok {
            return slv.Interface()
        }
        slv = reflect.Append(slv, v)
    }
}

func main() {
    ch := make(chan int)
    go func() {
        for i := 0; i < 10; i++ {
            ch <- i
        }
        close(ch)
    }()
    sl := ChanToSlice(ch).([]int)
    fmt.Println(sl)
}