Example: io.SectionReader
You have a large file and need to process only one byte range without loading the entire file into memory.
io.SectionReader gives you a bounded, seekable view of that range.
The important idea is:
io.SectionReadercreates a bounded view. It does not copy the data.
Quick Example
Read bytes 10 through 14 from a larger source:
The section starts at offset 10 in the underlying reader and exposes 5 bytes:
io.ReadAll handles io.EOF internally, so the example does not need special EOF handling.
When using Read directly, remember that io.EOF normally means the section has no more bytes to return. It is not necessarily an error condition.
What SectionReader Provides
A SectionReader has three important properties:
- bounded — it cannot read beyond the specified section
- seekable — it supports
Seek - random-access capable — it supports
ReadAt
The section is defined by:
where:
The underlying reader must implement io.ReaderAt.
The section's offsets are relative to the section, not the underlying source.
For example:
This makes a section behave like an independent byte range.
Read and ReadAt Use Different Positions
Read maintains the section's current position:
Each successful read advances that position.
ReadAt instead uses an explicit offset:
The offset 8 is relative to the section.
It does not change the section's current position.
This distinction is useful when code needs both sequential processing and explicit chunk access.
The Section Is Not a Copy
Creating a section does not read the selected bytes into memory.
This does not allocate a 1 MiB buffer containing the section.
Instead, the SectionReader keeps the boundaries and translates operations to the underlying ReaderAt.
That makes it useful for large files, where copying each requested region would be unnecessary.
Concurrent Chunk Processing
A common use case is processing different regions of a large file concurrently.
Each worker can receive its own SectionReader:
For example:
Each goroutine gets a different view:
The important point is not that SectionReader makes a reader magically concurrent.
The point is that each section has its own logical position, while the underlying ReaderAt provides the random-access operation.
For example, *os.File supports concurrent ReadAt operations.
SectionReader itself is not a general-purpose synchronization mechanism. Sharing one SectionReader between concurrent sequential Read operations requires synchronization.
SectionReader Does Not Own the Source
Creating a section does not transfer ownership:
Closing the file closes the underlying resource.
There is no separate Close on SectionReader.
This matters when designing APIs: the code that opens the file should normally remain responsible for its lifetime.
SectionReader vs LimitReader
These two APIs both restrict reads, but they solve different problems.
With LimitReader:
you start reading from the source's current position.
With SectionReader:
you explicitly select a byte range.
Use LimitReader when you need a byte limit.
Use SectionReader when you need a specific region of a random-access source.
Common Mistakes
Mistake 1: Treating the section as a copy
does not copy the data.
The underlying source is still accessed when the section is read.
Mistake 2: Expecting reads to continue past the section
If the section contains 100 bytes, reading the 101st byte does not continue into the rest of the file.
The boundary is part of the abstraction.
Mistake 3: Sharing one SectionReader between workers
A SectionReader has a current position.
This is problematic:
if both workers perform sequential Read operations on the same instance.
Create separate sections when workers need independent positions.
Mistake 4: Assuming SectionReader closes the file
It does not own the underlying resource.
The original ReaderAt remains responsible for its lifetime.
Mistake 5: Using it with a purely sequential source
SectionReader requires io.ReaderAt.
If the source only supports sequential reads, such as a network stream, SectionReader is not the right abstraction.
Rule of Thumb
Use
io.SectionReaderwhen you need a fixed, seekable view over a byte range of a random-access source.
It gives you bounded access without copying the underlying data, and separate sections can be used to process different ranges independently.