Currently I'm writing a program that is reading a buffer from bytes.Buffer. Its supposed to stop reading when it finds the character e. But when I was reading the buffer using a for loop I noticed something odd. When I put the byte reading as part of the for statement I get an infinity loop (example in go playground):
b := bytes.NewBuffer([]byte("uoiea"))
for v, _ := b.ReadByte(); v != 'e'; {
println("The value is " + string(v))
}
But if I removed it and put it inside the for loop, it doesn't (example in go playground):
b := bytes.NewBuffer([]byte("uoiea"))
for ;; {
v, _ := b.ReadByte()
println("The value is " + string(v))
if v == 'e'{
break
}
}
Does anyone know why is this? I find it that adding the break expression is a very ugly and error prone way to solve this.
Because your post statement of the for-loop is empty (you have only init statement), so you don't read next byte every iteration (v
is always 'u'
).
Here is the fixed version:
b := bytes.NewBuffer([]byte("uoiea"))
for v, _ := b.ReadByte(); v != 'e'; v, _ = b.ReadByte() {
println("The value is " + string(v))
}
As mentioned in comments, it's also recommended to check the error to avoid infinite loop when there is no 'e'
byte:
b := bytes.NewBuffer([]byte("uoiea"))
for v, e := b.ReadByte(); v != 'e' && e == nil; v, e = b.ReadByte() {
println("The value is " + string(v))
}
ReadBytes
returns io.EOF
error when buffer is empty.
You are only reading the buffer once, when you are entering the loop. So the value of v
will always be 'u'
. You could solve it by reading the next byte in for loop's post statement also:
for v, _ := b.ReadByte(); v != 'e'; v, _ = b.ReadByte() {
// This works as long as there is an 'e' in your buffer
// Otherwise this goes to infinite loop also, as you are discarding error
But your second example is actually better and less ugly way to do this. There's nothing wrong with using break
in loops like this. Actually it's very idiomatic in Go to write Read()
loops as conditionless for
loops and break
from the loop when appropriate (usually when you get an io.EOF
error indicating you reached the end of whatever you are reading). It leads to code that is easier to read, especially when there are many conditions that should cause break
. The idiomatic and preferred (IMHO) way to write the code would be this:
b := bytes.NewBuffer([]byte("uoiea"))
for {
v, err := b.ReadByte()
if err == io.EOF {
// End of buffer. Should always break here to avoid infinite loop.
break
}
if err != nil {
// Error other than io.EOF means something went wrong with reading.
// Should handle it appropriately. Here I'll just panic.
panic(err)
}
if v == 'e' {
break
}
println("The value is " + string(v))
}
Which is almost the same as your working example. Just with error checks. But the io.EOF
error check especially is very important, otherwise you''ll be stuck in infinite loop if there is no 'e'
in your buffer.