0Pricing
Go Academy · Lesson

Implementing Custom Readers

Build your own streams.

Implementing Custom Readers is a free Go Academy lesson on CoddyKit — lesson 4 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Go Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

Build Your Own Streams

You can create custom data sources by implementing the one-method io.Reader interface. Your type then works with every io helper.

The Method to Implement

Implement Read(p []byte) (n int, err error) on your type. Fill p, return the count, and return io.EOF when done.

A Constant Reader

Here a reader that endlessly emits the same byte, capped with LimitReader.

package main

import (
	"fmt"
	"io"
)

type constReader struct{ ch byte }

func (c constReader) Read(p []byte) (int, error) {
	for i := range p {
		p[i] = c.ch
	}
	return len(p), nil
}

func main() {
	r := io.LimitReader(constReader{ch: 'A'}, 5)
	data, _ := io.ReadAll(r)
	fmt.Println(string(data))
}

Signalling EOF

Return io.EOF once your source is exhausted. You may return it alone, or with the final bytes.

package main

import (
	"fmt"
	"io"
)

type oneShot struct{ done bool }

func (o *oneShot) Read(p []byte) (int, error) {
	if o.done {
		return 0, io.EOF
	}
	o.done = true
	n := copy(p, []byte("once"))
	return n, nil
}

func main() {
	data, _ := io.ReadAll(&oneShot{})
	fmt.Println(string(data))
}

Reading from a Slice

A custom reader can track a position into backing data, returning a chunk per call.

package main

import (
	"fmt"
	"io"
)

type sliceReader struct {
	data []byte
	pos  int
}

func (s *sliceReader) Read(p []byte) (int, error) {
	if s.pos >= len(s.data) {
		return 0, io.EOF
	}
	n := copy(p, s.data[s.pos:])
	s.pos += n
	return n, nil
}

func main() {
	r := &sliceReader{data: []byte("custom")}
	out, _ := io.ReadAll(r)
	fmt.Println(string(out))
}

A Transforming Reader

Wrap another reader and transform bytes on the fly — here uppercasing each byte as it streams.

package main

import (
	"bytes"
	"fmt"
	"io"
)

type upper struct{ r io.Reader }

func (u upper) Read(p []byte) (int, error) {
	n, err := u.r.Read(p)
	for i := 0; i < n; i++ {
		if p[i] >= 'a' && p[i] <= 'z' {
			p[i] -= 32
		}
	}
	return n, err
}

func main() {
	out, _ := io.ReadAll(upper{r: bytes.NewReader([]byte("hello"))})
	fmt.Println(string(out))
}

Respect the Buffer Length

Never write more than len(p) bytes. Use copy(p, src) which is bounded by the smaller slice, and return what it copied.

Value vs Pointer Receivers

If your reader tracks position or state, use a pointer receiver so mutations persist across calls. Stateless readers can use a value receiver.

Composing with io Helpers

Because your type satisfies io.Reader, it slots into io.Copy, bufio.NewReader, io.LimitReader, and more for free.

package main

import (
	"io"
	"os"
)

type dots struct{ left int }

func (d *dots) Read(p []byte) (int, error) {
	if d.left == 0 {
		return 0, io.EOF
	}
	p[0] = '.'
	d.left--
	return 1, nil
}

func main() {
	io.Copy(os.Stdout, &dots{left: 4})
	os.Stdout.WriteString("\n")
}

Common Pitfalls

  • Forgetting to return io.EOF (infinite read)
  • Writing past len(p)
  • Using a value receiver for stateful readers

Why Custom Readers

They let you adapt any source — generators, decoders, rate limiters, encrypted streams — into the universal Go I/O ecosystem.

Quick Check

Test your custom reader knowledge.

Recap

You learned to build your own streams.

  • Implement Read(p []byte) (int, error)
  • Use copy and respect len(p)
  • Return io.EOF when done
  • Pointer receiver for stateful readers
  • Compose with all io helpers for free

Frequently asked questions

Is the “Implementing Custom Readers” lesson free?

Yes — the full text of “Implementing Custom Readers” is free to read here on the web, and the Go Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Go Academy course, upgrade to CoddyKit PRO.

What will I learn in “Implementing Custom Readers”?

Build your own streams. You practise Go Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start Go Academy?

No prior experience is required. Go Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 4 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “Implementing Custom Readers” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this Go Academy lesson?

Yes. Every Go Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. The Reader Interface
  2. The Writer Interface
  3. io Utility Functions
  4. Implementing Custom Readers
← Back to Go Academy