String Concatenation in Go: How Not to Kill Performance

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Why Can Simple String Concatenation in Go Become a Problem?

\n\n

Everyone who starts learning Go sooner or later faces a task: you need to glue several strings into one. The most obvious way is to use the += operator or simple addition +. And it works… until you try to glue together a thousand strings. Suddenly your code starts to slow down, and memory consumption goes through the roof.

\n\n

Why does this happen? In Go, strings are immutable. When you write s += "new", Go does not append text to the end of the old string. It creates a new string, copying the contents of both parts into it. Each operation allocates new memory. If there are thousands of such operations, the garbage collector starts working in emergency mode, and performance drops.

\n\n

In this article, we will cover all the ways to concatenate strings in Go: from naive to production-ready. You will learn which method to use and when, and will be able to write code that you won't be ashamed to show at a code review.

\n\n

String Concatenation Methods in Go: From Simple to Efficient

\n\n

Let's look at five main approaches. For each, I will provide a code example and explain in which situations it is appropriate.

\n\n

1. The + and += Operators (Naive Method)

\n\n

The simplest and most intuitive method. Ideal for gluing together 2–5 strings.

\n\n
package main\n\nimport "fmt"\n\nfunc main() {\n    hello := "Hello"\n    world := "World"\n    result := hello + ", " + world + "!"\n    fmt.Println(result) // Hello, World!\n}\n
\n\n

When to use: only for a small number of strings. If you are writing a loop with 1000 iterations using += — stop. This has quadratic memory complexity (O(n²)).

\n\n

2. fmt.Sprintf

\n\n

Convenient when you need to format output. Internally, it also creates new strings, but does so more efficiently than manual gluing.

\n\n
name := "Alice"\nage := 30\nresult := fmt.Sprintf("My name is %s and I am %d years old.", name, age)\nfmt.Println(result)\n
\n\n

When to use: when you need not just to glue, but to format a string with different data types. For simple concatenation, it's not the best choice due to the overhead of parsing the template.

\n\n

3. strings.Join — The King for String Slices

\n\n

If you already have an array or slice of strings, strings.Join is the fastest and most elegant way.

\n\n
package main\n\nimport (\n    "fmt"\n    "strings"\n)\n\nfunc main() {\n    words := []string{"Go", "is", "awesome", "for", "backend"}\n    result := strings.Join(words, " ")\n    fmt.Println(result) // Go is awesome for backend\n}\n
\n\n

Internally, strings.Join first calculates the total length of the result, allocates memory once, and fills it. No unnecessary copying.

\n\n

When to use: whenever you have a ready-made slice of strings. This is the ideal option for building SQL queries, CSV strings, and URLs.

\n\n

4. bytes.Buffer — A Classic

\n\n

The bytes package provides a buffer that grows dynamically. It implements the io.Writer interface, so you can write to it using WriteString.

\n\n
package main\n\nimport (\n    "bytes"\n    "fmt"\n)\n\nfunc main() {\n    var buffer bytes.Buffer\n\n    for i := 0; i < 5; i++ {\n        buffer.WriteString("Line ")\n        buffer.WriteString(fmt.Sprintf("%d\\n", i))\n    }\n\n    fmt.Println(buffer.String())\n}\n
\n\n

The buffer allocates memory in chunks (usually 64 bytes), which significantly reduces the number of allocations compared to +=.

\n\n

When to use: in loops where the number of strings is not known in advance. For example, when generating HTML or logs.

\n\n

5. strings.Builder — The Modern Standard (Go 1.10+)

\n\n

Starting with Go 1.10, a special

Blogs

Book Recommendations