Example: io.NopCloser
io.NopCloser wraps an io.Reader and returns an io.ReadCloser with a no-op Close method.
It is a lightweight interface adapter used when an API strictly requires an io.ReadCloser, but the underlying data source (such as an in-memory *strings.Reader, *bytes.Reader, or *bytes.Buffer) does not expose a meaningful Close operation.
Key Characteristics
io.NopCloser does not:
- Read from the source
- Buffer data
- Copy data
- Close or modify the underlying source
It merely adapts the interface presented to the caller.
Common Use Cases
- Constructing an
http.Responsewith an in-memory body for tests or custom proxies. - Satisfying strict interface contracts when building middleware that replaces a request or response body with in-memory data.
- Bridging an
io.Readerto anio.ReadCloserwithout copying or buffering memory.
Quick Start
The simplest scenario: adapting an in-memory reader to satisfy an io.ReadCloser requirement.
Output:
Core Semantics
io.NopCloseradaptsio.Readertoio.ReadCloser; it does not transfer ownership, manage resources, or propagateCloseto the underlying reader. It allows a caller to satisfy anio.ReadClosercontract when the underlying reader has no meaningful close operation by makingClosea deliberate no-op.
Behavior: Close Is Not Propagated
Calling NopCloser.Close does not call Close or otherwise modify the wrapped reader.
Output:
io.NopCloser.Close does not propagate to the wrapped reader. It simply returns nil. Whether the underlying reader remains usable after calling Close is determined entirely by the implementation of that reader, not by io.NopCloser.
Constructing In-Memory HTTP Responses
When testing code that consumes an http.Response or when building proxy servers, you often need to construct a response body using static JSON or text.
Because http.Response.Body strictly requires an io.ReadCloser, io.NopCloser is the standard adapter for this job.
Note: The example above constructs a minimal, usable http.Response. A real http.Response returned by http.Client typically includes other fields like Status, Header, ContentLength, and Request.
What about http.Request?
While http.Response.Body requires manual wrapping for in-memory readers, **you usually do not need io.NopCloser when constructing an http.Request**.
Functions like http.NewRequest and http.NewRequestWithContext accept a standard io.Reader. The net/http package recognizes common in-memory reader types such as *bytes.Buffer, *bytes.Reader, and *strings.Reader and constructs the request body appropriately, so callers normally do not need to wrap them with io.NopCloser.
Conceptual Implementation
Conceptually, io.NopCloser is implemented as a small wrapper around the original reader. The wrapper forwards Read calls and provides a Close method that returns nil.
By embedding the io.Reader interface inside the nopCloser struct, Go automatically promotes the Read method. It is functionally equivalent to writing:
The explicit Close() method satisfies the io.Closer interface by returning nil.
(Note: Modern versions of the Go standard library preserve io.WriterTo when the wrapped reader implements it, allowing downstream operations such as io.Copy to retain the corresponding fast path.)
⚠️ Pitfall: Suppressing Required Resource Cleanup
The most dangerous anti-pattern when using io.NopCloser is wrapping it around a reader that actually possesses resources that need to be closed, such as an *os.File or a net.Conn.
Never use io.NopCloser to suppress a required resource cleanup.
The Anti-Pattern
Because the wrapper defines its own Close method, calling rc.Close() invokes nopCloser.Close() rather than file.Close(). The file descriptor therefore remains open unless file.Close() is called separately, leading to resource leaks in server applications.
Decision Matrix
Use the following matrix to determine if io.NopCloser is the correct choice for your scenario: