Go

How can you convert between []byte and string without allocating? When is it safe?

Question 398HardGo 1.22 to 1.25

A normal string(b) or []byte(s) copies because strings must be immutable. The compiler already avoids the copy in several cases:

  • Map lookups: m[string(b)] (lookup only, not assignment).
  • Comparisons and concatenation operands: string(b) == "GET".
  • for i, r := range string(b).
  • Small results that don't escape can use a 32-byte stack buffer.

For explicit zero-copy, Go 1.20 added unsafe.String, unsafe.StringData, unsafe.Slice:

// Safe only if b is never modified afterwards.
func bytesToString(b []byte) string {
    if len(b) == 0 {
        return ""
    }
    return unsafe.String(&b[0], len(b))
}

// The result must NEVER be written to: string memory may be read-only.
func stringToBytes(s string) []byte {
    return unsafe.Slice(unsafe.StringData(s), len(s))
}

// counts[string(l)]++ would allocate a key copy on EVERY iteration:
// the no-copy optimization applies only to pure lookups.
func countMethods(lines [][]byte) map[string]*int {
    counts := make(map[string]*int)
    for _, l := range lines {
        if p, ok := counts[string(l)]; ok { // pure lookup: no allocation
            *p++
            continue
        }
        n := 1
        counts[string(l)] = &n // key copied only when inserting a new key
    }
    return counts
}

Danger: if the byte slice is later mutated (e.g. it's a reused read buffer), the "immutable" string changes under you — a map key could silently corrupt the map. Writing into bytes derived from a string literal segfaults. Prefer APIs that avoid the conversion entirely: bytes package functions, strconv.AppendInt, io.StringWriter, w.Write with []byte. Interviewer wants: "I'd reach for unsafe only with a benchmark proving the win and the lifetime clearly documented."

More on Performance, Profiling & Testing

All 38 Performance, Profiling & Testing questions