Example: io.MultiWriter
io.MultiWriter makes multiple io.Writer values behave like one writer.
Each call to Write is sent to the writers in order.
The important boundary is:
io.MultiWriterduplicates writes synchronously. It does not provide buffering, concurrency, retry, or rollback.
Quick Example
The same bytes are written to stdout and the file.
MultiWriter does not create a second copy of the data or buffer it internally. It simply forwards each Write call to the underlying writers.
How It Behaves
Given:
a write behaves conceptually like:
The writers are processed sequentially and in the order provided.
If a writer returns an error, MultiWriter stops and does not call the remaining writers.
A writer that returns a short write without an error causes io.ErrShortWrite.
This makes MultiWriter suitable for straightforward fan-out, but not for independent delivery guarantees.
Common Use
Logging to stdout and a file
Writing data while calculating a hash
A hash implementation such as sha256.Hash is an io.Writer, so it can receive the same stream while the primary destination is being written:
This avoids reading the source twice.
Other useful combinations include:
- writing a generated stream to two files
- saving a local copy while sending data elsewhere
- recording a stream while passing it to its primary destination
Common Mistakes
1. Assuming writes are concurrent
They are not.
If w2 is slow, the caller waits for w2 before w3 is attempted.
If destinations have independent latency or failure requirements, use separate asynchronous pipelines instead of putting them behind one MultiWriter.
2. Treating a failure as transactional
MultiWriter does not roll back successful writes.
For example:
The caller gets an error, but w1 has already received the data.
This matters when retrying the operation. A retry may send the same bytes to w1 again.
MultiWriter therefore works best when duplicate delivery or partial delivery is acceptable, or when the application has an explicit recovery strategy.
3. Assuming n describes every destination
The returned byte count is not a commit record for the entire fan-out.
If an earlier writer succeeds and a later writer fails, n reflects the write that produced the returned result; it does not tell you how much data earlier writers have already accepted.
Do not use n == 0 as proof that no destination received data.
4. Forgetting resource ownership
MultiWriter only implements io.Writer. It does not close its underlying writers.
If you pass an io.WriteCloser, the caller still owns its lifecycle:
API Selection
Rule of Thumb
Use
io.MultiWriterwhen several destinations should receive the same stream synchronously.
It is a writer composition primitive, not a replication, buffering, or delivery system.