Standard library patterns · 3 MIN READ
Borrow bytes briefly; copy before their owner changes
Trace bytes.Buffer and Scanner views, then make retained results independent at a lifetime boundary.
A byte slice can be a borrowed window rather than an owned result. bytes.Buffer.Bytes exposes unread storage, and Scanner.Bytes exposes a token with a limited lifetime. The type []byte does not express that lifetime. Draw the owner, the view and the next mutation before deciding whether to copy.
Byte ownership · A MAP OF THE IDEA
- Buffer owns storage
- Bytes borrows a view
- Clone retained result
- Owner may reset
Useful when
- Borrow for immediate synchronous work
- Copy only at a lifetime boundary
- Document validity in the API
Watch out
- Clip does not copy
- Sending a slice does not freeze it
- Reset is not secret erasure
Implementation & operation cost: One deliberate copy buys independent result lifetime. Measure the boundary rather than guessing from allocation count.
One owner, one temporary view
Draw a row of bytes for the buffer, then a window labeled view above it. Bytes does not create a separate row. While the borrow is valid, changing an element through the view changes the buffer. A separate copy belongs on a second row with no shared storage.
package main
import (
"bytes"
"fmt"
)
func main() {
var buffer bytes.Buffer
buffer.WriteString("alpha")
view := buffer.Bytes()
owned := bytes.Clone(view)
view[0] = 'A'
fmt.Println(buffer.String())
buffer.Reset()
buffer.WriteString("omega")
fmt.Println(string(owned))
}The output is Alpha, then alpha. The first print demonstrates aliasing before reset. bytes.Clone creates the owned result before the owner changes. The program deliberately never inspects view after reset: its old contents are not a valid result to rely on.
The lifetime timeline
Follow this order: obtain a view, consume it synchronously, then mutate or release the owner. If you must return, cache or enqueue the result beyond that boundary, copy the bytes or transfer the owner explicitly. Assigning the slice to another variable copies its descriptor and preserves sharing. Clip also keeps the existing elements shared.
Scanner tokens need the same decision
Scanner.Bytes may expose storage reused by the next Scan. A token consumed immediately can remain borrowed. A token saved in a collection needs its own bytes. Check Scanner.Err after the loop as a separate requirement; correct ownership does not make input parsing infallible.
package main
import (
"bufio"
"bytes"
"fmt"
"strings"
)
func main() {
scanner := bufio.NewScanner(strings.NewReader("red\nblue\n"))
var saved [][]byte
for scanner.Scan() {
saved = append(saved, bytes.Clone(scanner.Bytes()))
}
if err := scanner.Err(); err != nil {
panic(err)
}
for _, token := range saved {
fmt.Println(string(token))
}
}Each iteration copies the current token before the next Scan can replace it. The saved values therefore print red and blue. Without a copy, storing the slice descriptors would not establish a durable token contract. The final error check handles scanning failures independently.
Asynchronous work crosses the boundary
A successful channel send transfers the slice descriptor, not an immutable snapshot. The producer must stop using the storage until consumption finishes, or send an owned copy. For cancellation, specify who owns a buffer when enqueueing fails and who releases it after a successful handoff.
Reset, retention and tests
Reset prepares a buffer for reuse and can retain its allocation. It does not guarantee erasure of sensitive bytes. Test that an owned result remains unchanged after later buffer operations. Avoid tests that require a particular stale value from an invalid view, and measure retained memory when large buffers live much longer than their input.
TAKE THIS WITH YOU
A borrowed result is safe only within its documented lifetime. Draw the next mutation or release, then copy before that boundary when the caller needs a durable value. Test independence of valid results rather than the contents of invalid views.
Check your understanding
A result must survive bytes.Buffer.Reset and later writes. What establishes independent ownership?
Go to the source
Progress stays in this browser. No account needed.