Go

How do you do zero-copy []byte ↔ string conversion correctly, and what can go wrong?

Question 353HardGo 1.22 to 1.25

Since Go 1.20, use the dedicated functions and not the old header casts:

// BytesToString: b must not be modified while the string is in use.
func BytesToString(b []byte) string {
    return unsafe.String(unsafe.SliceData(b), len(b))
}

// StringToBytes: the result must NEVER be written to.
func StringToBytes(s string) []byte {
    return unsafe.Slice(unsafe.StringData(s), len(s))
}

What goes wrong:

  • Mutating a string's bytes: string literals live in read-only memory, so writing to StringToBytes("hi")[0] is a segfault that recover cannot catch. For heap-allocated strings, mutation silently breaks immutability: map keys become corrupted and interned strings change behind other code.
  • Reusing the byte buffer: if b comes from a pool or bufio.Scanner.Bytes(), the "string" changes when the buffer is refilled. Storing it as a map key corrupts the map.
  • Empty input: unsafe.StringData("") may be nil, which is still fine to pass to unsafe.Slice with length 0.
  • append on the result of StringToBytes is safe only because cap equals len, so it must reallocate. Never extend it by reslicing.

What the interviewer is looking for: "measure first". The compiler already removes many copies, as described above. Use unsafe conversion only in a proven hot path with strict ownership rules, keep it in one small audited helper, and document the invariant.

More on Memory, GC & Runtime Internals

All 38 Memory, GC & Runtime Internals questions