The Avid Gopher

Efficient string manipulation in Go

String are nearly everywhere when developing software. But there is a confusing number of variants to deal with them. Let’s have a look.

Simple concatenation

A simple string concatenation might look like this:

1
fmt.Println("Hello " + "World!")

Although this little line of code might compile and execute without any problems it’s a very bad style. Why, you ask?

Strings in Go are immutable. You might think of them as constants. Once you initialised a string it can’t get changed afterwards. This leads to multiple memory allocations and copy operations with wastes some CPU cycles.

The standard Go compiler already makes some optimisations for string concatenations by using the + operator. So generally, using + operator to concatenate strings is convenient and efficient if the number of the concatenated strings is known at compile time.

But let me show a dynamic and more inefficient one, where to compile can’t apply any optimisations:

1
2
3
4
var myString string
myString += "Hello "
myString += "World!"
fmt.Println(myString.String())

The better bytes.Buffer approach (< Go 1.10)

1
2
3
4
var buf bytes.Buffer
buf.WriteString("Hello ")
buf.WriteString("World!")
fmt.Println(buf.String())

The even better strings.Builder approach (since Go 1.10)

Since Go version 1.10 strings.Builder is around, which helps us dealing efficiently with strings. The interface to the Builder is nearly the same as for the bytes.Buffer.

1
2
3
4
Write(p []byte) (int, error)
WriteByte(c byte) error
WriteRune(r rune) (int, error)
WriteString(s string) (int, error)

A basic example:

1
2
3
4
var builder strings.Builder
builder.WriteString("Hello ")
builder.WriteString("World!")
fmt.Println(builder.String())

Comparing with the above bytes.Buffer way, this method avoids making an unnecessary copying of the underlying bytes when preparing the result string.