loopsgochannelrune

Rune is received but struct is not received from chan


I have encountered odd behavior of golang 1.16. For one of my projects I needed to iterate over runes in string. So, I created a simple iterator looks like:

func iterRunes(s string) <-chan rune {
    runes := make(chan rune);
    go func() {
        defer close(runes);
        for _, char := range s {
            runes <- char;
        }
    } ()
    return runes;
}

It works perfectly, but I also needed to enumerate values yielding from this one. So, I have written another one looks like:

func enumRunes(runes <-chan rune) <-chan struct {int; rune} {
    eRunes := make(chan struct {int; rune});
    go func() {
        defer close(eRunes);
        i := 0;
        for r := range runes {
            eRunes <- struct {int; rune} {i, r};
            i++;
        }
    } ()
    return eRunes;
}

It also works perfectly. But if I try to combine them like enumRunes(iterRunes(pattern)), I encounter a problem. I have two code snippet to show.

The first one is:

    tmp := enumRunes(iterRunes(pattern))
    z := <-tmp;
    for z.int < utf8.RuneCountInString(pattern) {
        fmt.Println("z = <- tmp;")
        fmt.Println(z)
        z = <- tmp;
    }

And its output is like:

...
z = <- tmp;
{0 0}
{0 0}
z = <- tmp;
{0 0}
{0 0}
z = <- tmp;
{0 0}
{0 0}
z = <- tmp;
...

Here z is of type struct. So, I got into an infinite loop because the struct is not updated for some reason. And the second snippet:

    tmp := iterRunes(pattern)
    z := <-tmp;
    for qq := 0; qq < utf8.RuneCountInString(pattern); qq++ {
        fmt.Println("z = <- tmp;")
        fmt.Println(z)
        z = <- tmp;
    }

Here z is of type rune and it works fine. The question is why z in the first snippet isn't updated. Thank you in advance.


Solution

  • I hope you know that (ref)

    A receive from a closed channel returns the zero value immediately

    With that, let's look at your problem!

    z.int < utf8.RuneCountInString(pattern)

    Here this would always be satisfied as long as z.int is lesser than the rune count of the string, even if that value was a default 0.

    With this context, you can now see that even though the channel was closed, since you're reading from it, it'll return an empty {0, 0} struct. Which sets z.int to 0 and the loop continues to infinity.