What does the builtin clear(b) do to a []byte, and which copies of those bytes does it miss?
answer
- it writes, it does not shrink
- len and cap survive the call
- maps get entirely different treatment
- the conversion already made another copy
basics
~20 sclear(b) writes the zero value over every element from index 0 to len(b)-1, leaving len and cap unchanged; on a map it deletes all entries. It reaches only that one array, so copies made by conversions or append survive.
solid answer
~50 s`clear` is a builtin whose behaviour depends on what you hand it: for a slice it zeroes elements `[0:len(s))` — length and capacity are unchanged, and anything between `len` and `cap` is untouched — and for a map it deletes every entry. So `clear(key)` on a `[]byte` really does overwrite that array in place, unlike `key = nil` or `key = key[:0]`, which only drop or reslice a reference and leave the bytes where they were. What it cannot reach is every other copy Go made for you: `[]byte(s)` and `string(b)` both allocate and copy, so zeroing one side leaves the other intact; an `append` that outgrew capacity left the old array behind with the old contents; and formatting or buffering the value copies it again. In a heap dump or core file you find as many plaintext copies as the program made, not one.
code
go · 6 lineskey := make([]byte, 32)
// ... read the key into it, use it ...
clear(key) // key[0]..key[31] become 0; len(key) is still 32
b := []byte(os.Getenv("TOKEN"))
clear(b) // wipes this copy only; the environment string is unchangedgo deeper
Recall that clear works on both slices and maps and does different things to each: it zeroes a slice's elements in place and deletes all of a map's entries. Know that it does not change a slice's length.
Explain the exact range it zeroes and why b[:0] and b = nil are not equivalent, and connect it to Go's copying rules — []byte(s) and string(b) each allocate, so wiping one side leaves the other.
Argue about copy count rather than the wipe itself: preallocate to avoid append abandoning an array, keep the secret out of strings and formatters, and be honest that a core file will still show whatever copies existed.
Decide how much of this is worth mandating: where zeroing is required, where it is theatre, and what you would rather spend the engineering budget on to bound the exposure.
## The builtin `clear` takes a map, a slice, or a value of a type parameter constrained to those. Its two behaviours are different enough that the word alone is ambiguous: - **`clear(s)` on a slice** sets `s[i]` to the element type's zero value for every `i` in `[0, len(s))`. The slice header is not changed: `len(s)` and `cap(s)` are exactly what they were, and any elements between `len` and `cap` — the spare capacity — are *not* touched. - **`clear(m)` on a map** deletes all entries, leaving an empty but usable map. It also removes keys that a `delete` loop cannot reach, such as NaN float keys. `clear` on a nil map or nil slice is a no-op rather than a panic. For a credential held in a `[]byte`, the slice form is the one you want, and it is genuinely different from the things people write instead: ```go key = nil // drops the reference; the array keeps its contents until reused key = key[:0] // changes len only; the bytes are all still there, in cap clear(key) // actually overwrites key[0:len(key)] ``` ## Which copies it misses The interesting part of this question is not the builtin, it is Go's copying: **Conversions copy.** `[]byte(s)` allocates a new array and copies the string's bytes; `string(b)` allocates and copies the other way. A very common shape is `b := []byte(os.Getenv("TOKEN"))` followed by `clear(b)` — which zeroes the copy and leaves the original string exactly as it was. Go strings are immutable and there is no supported way to overwrite one, which is why a secret you intend to wipe should arrive as bytes (from `os.ReadFile`, a decoder, a network read) and never be converted to `string` on the way. **append growth abandons an array.** If a buffer holding secret material is grown past its capacity, `append` allocates a bigger array, copies the contents, and returns a slice pointing at the new one. The old array still holds the old bytes and your `clear` on the current slice cannot reach it. Preallocating with `make([]byte, 0, n)` at the known size avoids the abandoned copy. **Everything that formats or buffers copies.** Writing the value through a `bufio.Writer`, formatting it with `fmt`, encoding it to base64 or hex, or putting it in a `bytes.Buffer` all produce further copies with their own lifetimes. ## What the runtime does and does not do to help Go's garbage collector is non-moving for heap objects, so a heap-allocated `[]byte` stays at one address for its life and `clear` overwrites the only heap copy of that array. Goroutine stacks are a different story: they grow by allocating a larger stack and copying the old one, so a buffer that lives on the stack can leave a stale copy behind in memory that has been freed for reuse. Nothing in the language guarantees when freed memory is overwritten. That is why the honest framing of `clear` in a review is: it narrows a window, it does not close a hole. Grep a core file or a heap dump of a process that has handled a secret and you will find as many plaintext instances as the code made copies. Reducing the *number of copies* — read into one buffer, never convert to `string`, do not log it, do not put it in a long-lived struct — buys more than zeroing the last one, and zeroing is worth doing on top of that precisely because it makes the long-lived buffer stop being a copy the moment you are finished with it. A final practical note: `clear` runs at the point you call it. If the buffer's lifetime ends with a function, `defer` the wipe next to the read so the two are impossible to separate in review, and make sure nothing has squirrelled a subslice of the same array away somewhere that will now silently see zeroes.
- What is the difference between clear(b), b = b[:0] and b = nil for a []byte?`clear(b)` overwrites every element in `[0:len(b))` with zero and leaves the header alone. `b = b[:0]` only sets the length to zero — every byte is still present in the capacity and readable through any other slice of the same array. `b = nil` just drops this reference; the array keeps its contents until the collector reclaims it and something else reuses that memory.
- Why can you not do the same thing to a string holding the secret?Go strings are immutable: there is no index assignment and no builtin that writes into one, so the bytes behind a string cannot be overwritten through the language. The only route is to avoid the string in the first place — read the secret into a `[]byte` and keep it there — because `[]byte(s)` copies rather than aliases, so wiping the copy leaves the original intact.
- Does clear on a map free the memory the map was using?It deletes every entry and leaves an empty, reusable map, but the map keeps its allocated buckets, so the memory footprint does not necessarily drop. That is usually the point: `clear(m)` is the way to reuse a map without reallocating. If you actually want the memory released, drop the reference and let a fresh `make` allocate a right-sized map.
clear(b) is shredding the one printout on your desk. If you photocopied it on the way in, the copier tray still has the others.
saying these in an interview costs you the question
- Thinks clear(b) sets the slice length to zero
- Believes b = b[:0] overwrites the bytes
- Expects clear to zero the spare capacity beyond len
- Thinks clearing a []byte copy also wipes the source string
- Treats zeroing as a guarantee the plaintext is gone from memory