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.
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.