Efficient String Concatenation in Go
String concatenation appears everywhere in Go programs: generating text, building paths, formatting reports, or assembling protocol messages. The best technique depends on how many pieces you are joining and how the data is already structured.
1. The + Operator
For a small, fixed number of strings, + is clear and perfectly reasonable:
str1 := "Hello, "
str2 := "world!"
result := str1 + str2Avoid repeatedly growing a string with + inside a large loop, because each result is a new immutable string and repeated copying can become expensive.
2. strings.Join
When the pieces already live in a slice, strings.Join is usually the clearest option:
parts := []string{"Hello", "world", "from", "Go!"}
result := strings.Join(parts, " ")It is concise, takes a separator directly, and can allocate efficiently because it knows all inputs in advance.
3. bytes.Buffer
bytes.Buffer is useful when you are building bytes and strings together or when the result will also be consumed through byte-oriented APIs:
var buffer bytes.Buffer
buffer.WriteString("Hello, ")
buffer.WriteString("world!")
result := buffer.String()It remains a useful standard-library type; it is not merely a legacy option.
4. strings.Builder
When the final result is a string and you are writing many pieces incrementally, strings.Builder is often a good default:
var builder strings.Builder
builder.WriteString("Hello, ")
builder.WriteString("world!")
result := builder.String()If you can estimate the final size, call Grow first to reduce reallocations:
var builder strings.Builder
builder.Grow(128)
builder.WriteString("prefix: ")
builder.WriteString(value)
result := builder.String()Quick Comparison
| Method | Best fit |
|---|---|
+ |
A few fixed string expressions |
strings.Join |
Joining a slice with a separator |
bytes.Buffer |
Mixed byte/string construction or byte-oriented APIs |
strings.Builder |
Incrementally building a string, especially in loops |
Measure Before Optimizing
For small strings, readability matters more than tiny allocation differences. If concatenation is on a performance-sensitive path, benchmark the actual workload with Go’s testing package rather than assuming one technique will always be fastest.
Conclusion
Use + for simple expressions, strings.Join for slices, and strings.Builder when building a string incrementally. bytes.Buffer remains valuable when your work is naturally byte-oriented. Choosing the method that matches the data shape usually gives both readable code and good performance.