Go

What does converting between string and []byte cost, and how do you build strings efficiently?

Question 13HardGo 1.22 to 1.25

Converting []byte(s) or string(b) normally allocates and copies the data. This is required because strings are immutable and byte slices are not. The compiler skips the copy in several common cases:

  • map lookups with m[string(b)]
  • comparisons like string(b) == "x"
  • concatenation operands
  • for range []byte(s)
  • small conversions that don't escape (these use a stack buffer)

Repeated s += x in a loop is O(n²), because every step copies the whole string built so far. Use strings.Builder instead. Its String() method returns the internal buffer without copying.

func join(parts []string) string {
	var b strings.Builder
	n := 0
	for _, p := range parts {
		n += len(p)
	}
	b.Grow(n) // one allocation
	for _, p := range parts {
		b.WriteString(p)
	}
	return b.String()
}

// Zero-copy conversion (Go 1.20+). Only safe if the bytes are never modified afterwards:
func bytesToString(b []byte) string {
	return unsafe.String(unsafe.SliceData(b), len(b))
}

Gotchas:

  • A strings.Builder must not be copied after its first use. Writing to such a copy panics, because the Builder records its own address and checks it on every write.
  • bytes.Buffer can also read, so it's the right choice for I/O. strings.Builder is leaner for building a string.
  • For a small, fixed number of pieces, + or fmt.Sprintf is fine. Measure with a benchmark before you optimize.

More on Language Fundamentals & Types

All 39 Language Fundamentals & Types questions