Different behaviour of break vs. return in infinite loop when implementing io.Reader

Hi everyone,

This is my first post here. I’m learning Go and going through the official tour. Today, I met something weird while doing the rot13reader exercise.

The problem is when I use break instead of return ttl, io.EOF, the program goes into an infinite loop. However, as far as I know, in this program, there should be no difference with either break or return ttl, io.EOF because if it’s break, the next line will be return ttl, err at the end of the Read() method, which is the same as return ttl, io.EOF.

I’m wondering if there is anything to do with the underlying mechanism of how Go handles the io.Reader interface and its implementations.

Here is the code.

package main

import (

type rot13Reader struct {
	r io.Reader

func (rr *rot13Reader) Read(b []byte) (n int, err error) {
	rb := make([]byte, 8)
	var ttl int
	for {
		n, err := rr.r.Read(rb)
		if err == io.EOF {
			return ttl, io.EOF
			// break <----------------------------here's the problem
		} else if err != nil {
		} else {
			for i, c := range rb[:n] {
				b[i+ttl] = decodeRot13(c)
			ttl += n
	return ttl, err

func decodeRot13(c byte) byte {
	if c >= 97 && c <= 122 { // a-z: 97 122
		c += 13
		if c > 122 {
			c -= 26
	} else if c >= 65 && c <= 90 { // A-Z: 65 90
		c += 13
		if c > 90 {
			c -= 26
	return c

func main() {
	s := strings.NewReader("Lbh penpxrq gur pbqr!")
	r := rot13Reader{s}
	io.Copy(os.Stdout, &r)

You need to return EOF when you finish reading, it goes infinity loop because io.Copy waits EOF from the reader. Take a look at this marvelous article

Hi Ali. Yes, you are right. I need to return EOF. However, the root cause is more subtle. It’s because of variable shadowing. See the answer here on StackOverflow.

ahh ok didn’t see it, sorry

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.