Sienna
Sienna

Reputation: 1699

Golang - Sorting on fan in

I am randomly generating a bunch of log messages, and after they have been generated, I need to sort them by timestamp before writing them to the logs. I'm utilising the sort.Interface aspect of the sort library so I can sort based on my timestamp. I'm using a fan-in concurrency design, so my sorting function aggregates all the log messages from the goroutines, then sorts them.

Here is my code:

type CommonLogFormat struct {
    HostIP         string
    UserIdent      string
    User           string
    Timestamp      string
    Request        string
    HttpStatusCode int
    Size           int
}

type Logs struct {
    Messages []*CommonLogFormat
}

func sortByTimestamp(ch chan <- *CommonLogFormat) *Logs {
    logs := &Logs{Messages: make([]*CommonLogFormat, 1)}

    for i := range ch {
        logs.Messages = append(logs.Messages, <- i)
    }

    sort.Sort(logs)

    return logs
}

func (l Logs) Len() int {
    return len(l.Messages)
}

func (l Logs) Less(i,j int) bool {
    return l.Messages[i].Timestamp < l.Messages[j].Timestamp
}

func (l *Logs) Swap(i,j int) {
    l.Messages[i], l.Messages[j] = l.Messages[j], l.Messages[i]
}

However, when I go to receive a log message from the channel, I get this error:

invalid operation: <-i (receive from non-chan type *CommonLogFormat)

Why can't I receive a value from the channel?

Upvotes: 3

Views: 924

Answers (1)

icza
icza

Reputation: 417767

I think the error message is pretty self-explanatory. Look at this:

for i := range ch {
    logs.Messages = append(logs.Messages, <- i)
}

ch is of type chan <- *CommonLogFormat. ch is a channel. The for range loop over the channel yields the values sent on the channel, which will be stored in the loop variable i. i is not a channel, but the values sent on the channel, so it will be of type *CommonLogFormat.

So no need, and you actually can't receive from it, it is already what you would want to receive from it. Simply append i:

for i := range ch {
    logs.Messages = append(logs.Messages, i)
}

The Spec: For statements details what the loop variables are in case of for range:

Range expression                          1st value          2nd value

array or slice  a  [n]E, *[n]E, or []E    index    i  int    a[i]       E
string          s  string type            index    i  int    see below  rune
map             m  map[K]V                key      k  K      m[k]       V
channel         c  chan E, <-chan E       element  e  E

The last line applies in case of ranging over a channel, and the first iteration value is the element.

For channels, the iteration values produced are the successive values sent on the channel until the channel is closed. If the channel is nil, the range expression blocks forever.

Upvotes: 2

Related Questions