New Way:

From Go 1.10 there is a strings.Builder type, please take a look at this answer for more detail.

Old Way:

Use the bytes package. It has a Buffer type which implements io.Writer.

package main

import (
    "bytes"
    "fmt"
)

func main() {
    var buffer bytes.Buffer

    for i := 0; i < 1000; i++ {
        buffer.WriteString("a")
    }

    fmt.Println(buffer.String())
}

This does it in O(n) time.

Answer from marketer on Stack Overflow
🌐
Sentry
sentry.io › sentry answers › go › concatenating strings in go
Concatenating strings in Go | Sentry
October 15, 2024 - The simplest way to combine strings, especially when concatenating just a few values, is to use the + operator:
🌐
Reddit
reddit.com › r/golang › efficient string concatenation
r/golang on Reddit: efficient string concatenation
June 11, 2024 -
NOTE:  After discussion with this awesome subreddit, I realize I'm asking the wrong question.  I don't need a string builder.  I'm optmizing just for the sake of optimizing, which is wrong.  So will just stick to + operator. 

Thank you all for the feedback !

I'm aware of strings.Builder but here is my confusion.

I need to use some string variables. My first thought was to do this:

var s strings.Builder
name := "john"
s.WriteString("hello " + name)
fmt.Println(s.String())

Dumb question, is still wrong to use + ? Or should I do this:

var s strings.Builder
name := "john"
s.WriteString("hello ")
s.WriteString(name)
fmt.Println(s.String())

EDIT1: adding bechmarks.

code:

concat_test.go

package main

import (
	"strings"
	"testing"
)

func BenchmarkConcatAndWrite(b *testing.B) {
	var s strings.Builder
	name := "john"
        b.ReportAllocs()
	for i := 0; i < b.N; i++ {
		s.Reset()
		s.WriteString("hello " + name)
	}
}

func BenchmarkSeparateWrites(b *testing.B) {
	var s strings.Builder
	name := "john"
        b.ReportAllocs()
	for i := 0; i < b.N; i++ {
		s.Reset()
		s.WriteString("hello ")
		s.WriteString(name)
	}
}

results:

go test -bench=.
goos: darwin
goarch: amd64
pkg: test
cpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
BenchmarkConcatAndWrite-12    	25422900	        44.04 ns/op	      16 B/op	       1 allocs/op
BenchmarkSeparateWrites-12    	26773579	        44.37 ns/op	      24 B/op	       2 allocs/op
PASS
ok  	test	2.518s

EDIT2: posting actual code and updated benchmark.

concat.go

package concat

import (
	"fmt"
	"strings"
)

type Metadata struct {
	NumReplica int `json:"num_replica"`
}

type IndexData struct {
	BucketId    string   `json:"bucket_id"`
	Condition   string   `json:"condition"`
	DatastoreId string   `json:"datastore_id"`
	Id          string   `json:"id"`
	IndexKey    []string `json:"index_key"`
	IsPrimary   bool     `json:"is_primary"`
	KeyspaceId  string   `json:"keyspace_id"`
	Metadata    Metadata `json:"metadata"`
	Name        string   `json:"name"`
	NamespaceId string   `json:"namespace_id"`
	Partition   string   `json:"partition"`
	ScopeId     string   `json:"scope_id"`
	State       string   `json:"state"`
	Using       string   `json:"using"`
}

func ConcatAndWrite(data IndexData) string {
	var indexDefinition strings.Builder

	switch data.IsPrimary {

	case false:
		indexDefinition.WriteString("CREATE INDEX " + data.Name + " ON ")
		indexDefinition.WriteString(data.BucketId + "." + data.ScopeId + "." + data.KeyspaceId)
		indexDefinition.WriteString("(")

		for i, ik := range data.IndexKey {
			if i > 0 {
				indexDefinition.WriteString(",")
			}
			indexDefinition.WriteString(ik)
		}
		indexDefinition.WriteString(")")

		if data.Partition != "" {
			indexDefinition.WriteString(" PARTITION BY " + data.Partition)
		}

		if data.Condition != "" {
			indexDefinition.WriteString(" WHERE " + data.Condition)
		}

	case true:
		indexDefinition.WriteString("CREATE PRIMARY INDEX ")

		if data.Name != "#primary" {
			indexDefinition.WriteString(data.Name + " ")
		}

		indexDefinition.WriteString("ON " + data.BucketId + "." + data.ScopeId + "." + data.KeyspaceId)
	}

	if data.Metadata.NumReplica > 0 {
		replicas := fmt.Sprint(data.Metadata.NumReplica)
		indexDefinition.WriteString(" WITH {\"num_replica\":" + replicas + "\"}")
	}

	return indexDefinition.String()
}

func NoConcat(data IndexData) string {
	var indexDefinition strings.Builder

	switch data.IsPrimary {

	case false:
		indexDefinition.WriteString("CREATE INDEX ")
		indexDefinition.WriteString(data.Name)
		indexDefinition.WriteString(" ON ")
		indexDefinition.WriteString(data.BucketId)
		indexDefinition.WriteString(".")
		indexDefinition.WriteString(data.ScopeId)
		indexDefinition.WriteString(".")
		indexDefinition.WriteString(data.KeyspaceId)
		indexDefinition.WriteString("(")

		for i, ik := range data.IndexKey {
			if i > 0 {
				indexDefinition.WriteString(",")
			}
			indexDefinition.WriteString(ik)
		}
		indexDefinition.WriteString(")")

		if data.Partition != "" {
			indexDefinition.WriteString(" PARTITION BY ")
			indexDefinition.WriteString( data.Partition)
		}

		if data.Condition != "" {
			indexDefinition.WriteString(" WHERE ")
			indexDefinition.WriteString(data.Condition)
		}

	case true:
		indexDefinition.WriteString("CREATE PRIMARY INDEX ")

		if data.Name != "#primary" {
			indexDefinition.WriteString(data.Name)
			indexDefinition.WriteString( " ")
		}

		indexDefinition.WriteString("ON ")
		indexDefinition.WriteString(data.BucketId)
		indexDefinition.WriteString(".")
		indexDefinition.WriteString(data.ScopeId)
		indexDefinition.WriteString(".")
		indexDefinition.WriteString(data.KeyspaceId)
	}

	if data.Metadata.NumReplica > 0 {
		replicas := fmt.Sprint(data.Metadata.NumReplica)
		indexDefinition.WriteString(" WITH {\"num_replica\":")
		indexDefinition.WriteString(replicas )
		indexDefinition.WriteString("\"}")
	}

	return indexDefinition.String()
}

func ConcatPlusOperator(data IndexData) string {
	var indexDefinition string

	switch data.IsPrimary {
	case false:
		indexKeys := strings.Join(data.IndexKey, ",")
		indexDefinition += fmt.Sprintf("CREATE INDEX %s ON %s.%s.%s(%s)", data.Name, data.BucketId, data.ScopeId, data.KeyspaceId, indexKeys)

		if data.Partition != "" {
			indexDefinition += fmt.Sprintf(" PARTITION BY %s",data.Partition)
		}

		if data.Condition != "" {
			indexDefinition += fmt.Sprintf(" WHERE %s", data.Condition) 
		}

	case true:
		indexDefinition = "CREATE PRIMARY INDEX "

		if data.Name != "#primary" {
			indexDefinition += fmt.Sprintf("%s ", data.Name)
		}

		indexDefinition += fmt.Sprintf("ON %s.%s.%s", data.BucketId, data.ScopeId, data.KeyspaceId)
	}
	
	if data.Metadata.NumReplica > 0 {
		indexDefinition += fmt.Sprintf(" WITH {\"num_replica\": %d \"}", data.Metadata.NumReplica)
	}

	return indexDefinition
}

concat_test.go

package concat

import (
	"testing"
)

func BenchmarkConcatAndWrite(b *testing.B) {
	m := Metadata{NumReplica: 2}

	data := IndexData{
		BucketId:    "jobs",
		Condition:   "(`id` = 2)",
		DatastoreId: "http://127.0.0.1:8091",
		Id:          "a607ef2e22e0b436",
		IndexKey:    []string{"country", "name", "id"},
		KeyspaceId:  "c2",
		Metadata:    m,
		Name:        "idx3",
		NamespaceId: "default",
		Partition:   "HASH((meta().`id`))",
		ScopeId:     "s1",
		State:       "online",
		Using:       "gsi",
	}

	b.ReportAllocs()

	for i := 0; i < b.N; i++ {
		ConcatAndWrite(data)
	}
}

func BenchmarkNoConcat(b *testing.B) {
	m := Metadata{NumReplica: 2}

	data := IndexData{
		BucketId:    "jobs",
		Condition:   "(`id` = 2)",
		DatastoreId: "http://127.0.0.1:8091",
		Id:          "a607ef2e22e0b436",
		IndexKey:    []string{"country", "name", "id"},
		KeyspaceId:  "c2",
		Metadata:    m,
		Name:        "idx3",
		NamespaceId: "default",
		Partition:   "HASH((meta().`id`))",
		ScopeId:     "s1",
		State:       "online",
		Using:       "gsi",
	}

	b.ReportAllocs()

	for i := 0; i < b.N; i++ {
		NoConcat(data)
	}
}

func BenchmarkPlusOperator(b *testing.B) {
	m := Metadata{NumReplica: 2}

	data := IndexData{
		BucketId:    "jobs",
		Condition:   "(`id` = 2)",
		DatastoreId: "http://127.0.0.1:8091",
		Id:          "a607ef2e22e0b436",
		IndexKey:    []string{"country", "name", "id"},
		KeyspaceId:  "c2",
		Metadata:    m,
		Name:        "idx3",
		NamespaceId: "default",
		Partition:   "HASH((meta().`id`))",
		ScopeId:     "s1",
		State:       "online",
		Using:       "gsi",
	}

	b.ReportAllocs()

	for i := 0; i < b.N; i++ {
		ConcatPlusOperator(data)
	}
}

benchmarks:

go test -bench=.
goos: darwin
goarch: amd64
cpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
BenchmarkConcatAndWrite-12    	 2932362	       404.1 ns/op	     408 B/op	       5 allocs/op
BenchmarkNoConcat-12          	 4595264	       258.0 ns/op	     240 B/op	       4 allocs/op
BenchmarkPlusOperator-12      	 1343035	       890.4 ns/op	     616 B/op	      15 allocs/op
PASS
ok  	_/Users/hiteshwalia/go/src/local/test/concat	5.262s
Top answer
1 of 16
1095

New Way:

From Go 1.10 there is a strings.Builder type, please take a look at this answer for more detail.

Old Way:

Use the bytes package. It has a Buffer type which implements io.Writer.

package main

import (
    "bytes"
    "fmt"
)

func main() {
    var buffer bytes.Buffer

    for i := 0; i < 1000; i++ {
        buffer.WriteString("a")
    }

    fmt.Println(buffer.String())
}

This does it in O(n) time.

2 of 16
644

In Go 1.10+ there is strings.Builder, here.

A Builder is used to efficiently build a string using Write methods. It minimizes memory copying. The zero value is ready to use.


Example

It's almost the same with bytes.Buffer.

package main

import (
    "strings"
    "fmt"
)

func main() {
    // ZERO-VALUE:
    //
    // It's ready to use from the get-go.
    // You don't need to initialize it.
    var sb strings.Builder

    for i := 0; i < 1000; i++ {
        sb.WriteString("a")
    }

    fmt.Println(sb.String())
}

Click to see this on the playground.


Supported Interfaces

strings.Builder's methods are being implemented with the existing interfaces in mind so that you can switch to the new Builder type easily in your code.

Method Signature Interface Description
Grow(int) bytes.Buffer Grows the buffer's capacity by the specified amount. See bytes.Buffer#Grow for more information.
Len() int bytes.Buffer Returns the number of bytes in the buffer. See bytes.Buffer#Len for more information.
Reset() bytes.Buffer Resets the buffer to be empty. See bytes.Buffer#Reset for more information.
String() string fmt.Stringer Returns the contents of the buffer as a string. See fmt.Stringer for more information.
Write([]byte) (int, error) io.Writer Writes the given bytes to the buffer. See io.Writer for more information.
WriteByte(byte) error io.ByteWriter Writes the given byte to the buffer. See io.ByteWriter for more information.
WriteRune(rune) (int, error) bufio.Writer or bytes.Buffer Writes the given rune to the buffer. See bufio.Writer#WriteRune or bytes.Buffer#WriteRune for more information.
WriteString(string) (int, error) io.stringWriter Writes the given string to the buffer. See io.stringWriter for more information.

Differences from bytes.Buffer

  • It can only grow or reset.
  • It has a copyCheck mechanism built-in that prevents accidentally copying it. In bytes.Buffer, one can access the underlying bytes like this: (*Buffer).Bytes(). strings.Builder prevents this problem. Sometimes, this is not a problem, though, and is desired instead. For example: For the peeking behavior when the bytes are passed to an io.Reader etc.
  • bytes.Buffer.Reset() rewinds and reuses the underlying buffer whereas the strings.Builder.Reset() does not, it detaches the buffer.

Note

  • Do not copy a strings.Builder value as it caches the underlying data.
  • If you want to share a strings.Builder value, use a pointer to it.

Check out its source code for more details, here.

🌐
GeeksforGeeks
geeksforgeeks.org › go language › different-ways-to-concatenate-two-strings-in-golang
Different ways to concatenate two strings in Golang - GeeksforGeeks
October 28, 2024 - The bytes.Buffer approach allows efficient string concatenation without generating intermediate strings, using WriteString() to append each segment.
🌐
Max Chadwick
maxchadwick.xyz › blog › concatenate-a-string-and-an-int-in-go
Concatenate a string and an int in Go | Max Chadwick
May 24, 2019 - package main import ( "fmt" "strconv" "strings" ) func main() { num := 1 email := "[email protected]" concatenated := strings.Join([]string{strconv.Itoa(num), email}, "") fmt.Println(concatenated) }
🌐
DEV Community
dev.to › schadokar › the-fastest-way-to-concatenate-strings-in-golang-5mf
The fastest way to concatenate strings in Golang - DEV Community
April 30, 2024 - The + operator is the fastest way to concatenate strings and fmt.Sprintf is the slowest. <----------------Fastest-------------------- + > += > strings.Builder > fmt.Sprintf ----------------Slowest--------------------> ... I am a Software Engineer.
Find elsewhere
🌐
Programming Tips
programming-tips.info › concatenate_string_literal › go › en › index.html
Tips! Concatenate strings in Golang
Concatenate strings in Golang Concatenate string literals (string enclosed in double quotes). To concatenate strings simply, use + operator. However, if strings are concatenated by + operator, new string object is generated. If you use bytes.Buffer, string is stored in buffer.
🌐
Medium
medium.com › @arjun.devb25 › optimizing-string-merging-in-go-a-tale-of-two-solutions-6e6ccfef4c7f
Optimizing String Merging in Go: A Tale of Two Solutions | by Arjun Singh | Medium
January 1, 2025 - In summary, when merging strings in Go, always consider the impact of string concatenation inside loops and prefer using slices for efficient accumulation of results. This small change can make a huge difference in performance, especially in memory-intensive applications. This article outlines both the naive and optimized approaches, explaining the issue with the initial implementation and how to improve it for better performance. Golang ·
🌐
Golang
golang.company › blog › perform-string-concatenation-in-go
7 Ways to Perform String Concatenation in Go
August 18, 2022 - This is another simple method to concatenate a string in Golang. What this function will do is that it will combine different elements in the slice of a string to create a new string.
🌐
techwasti
techwasti.com › how-to-efficiently-concatenate-strings-in-go-language
String concatenation best practices in go language
May 2, 2023 - This approach is more efficient than using the + operator because it avoids creating intermediate strings. The most efficient way to concatenate strings in Go is to use the bytes.Buffer type.
🌐
W3Schools
w3schools.com › cpp › cpp_strings_concat.asp
C++ String Concatenation
The + operator can be used between strings to add them together to make a new string. This is called concatenation:
🌐
Calhoun
calhoun.io › 6-tips-for-using-strings-in-go
6 Tips for Using Strings in Go - Calhoun.io
Run it on the Go Playground → https://play.golang.org/p/mpIQuNhC1kr · Go 1.10 has a new strings.Builder! While the code below still works, Go 1.10 introduced the Builder type to the strings package which can be easier to use and is typically a little more efficient. I’ve written about using it in the article, Concatenating and Building Strings in Go 1.10+. While Go does allow you to concatenate strings with the + operator, this can become pretty inefficient when concatenating a lot of strings together.
🌐
Golang Docs
golangdocs.com › home › 7 ways to concatenate strings in golang
7 Ways to Concatenate Strings in Golang - Golang Docs
January 28, 2020 - The strings can be appended to one another by using the += operator. It is the same as above. But a slightly shorter way of concatenating. It appends the right side to the string it is operated on. So, it essentially appends string.
🌐
Bill Glover
billglover.me › 2019 › 03 › 13 › learn-go-by-concatenating-strings
Learn Go: by Concatenating Strings
September 28, 2025 - In the second loop, rawstringtmp(buf, l) returns a []byte slice and a string. This results in memory allocation and why we see one allocation per iteration of our concatenation function. Interestingly, the []byte and string point to the same underlying memory.
🌐
SegmentFault
segmentfault.com › a › 1190000040999541 › en
How to efficiently concatenate strings in Go language (comparative analysis of 6 ways) - 个人文章 - SegmentFault 思否
November 22, 2021 - In this paper we focused 6 be kind of splicing string introduction, and by benckmark comparing the efficiency whenever using strings.builder go wrong, but when a small amount of string concatenation, direct + is the better way, specific business scenarios Specific analysis, do not generalize. The code in the article has been uploaded github : https://github.com/asong2020/Golang_Dream/tree/master/code_demo/string_join
🌐
Calhoun
calhoun.io › concatenating-and-building-strings-in-go
Concatenating and Building Strings in Go 1.10+ - Calhoun.io
func join(strs ...string) string { var sb strings.Builder for _, str := range strs { sb.WriteString(str) } return sb.String() } Run it on the Go Playground → https://play.golang.org/p/1ggqB1f2Y6C
🌐
YourBasic
yourbasic.org › golang › build-append-concatenate-strings-efficiently
Efficient string concatenation [full guide] · YourBasic Go
yourbasic.org/golang · Clean and simple string building (fmt) High-performance string concatenation (stringbuilder) Before Go 1.10 (bytebuffer) For simple cases where performance is a non-issue, fmt.Sprintf is your friend. It’s clean, simple and fairly efficient.
🌐
Scaler
scaler.com › home › topics › golang › how to concatenate strings in golang?
How to Concatenate Strings in GoLang? - Scaler Topics
May 4, 2023 - The string is a slice of bytes ... is created it’s not possible to change it. The + operator performs golang string concatenation and += also performs concatenation....
🌐
Mohamed Allam
mohamedallam.tech › blog › concatenation-merge-strings-in-golang
Strings concatenation in Golang (merge strings) · Mohamed Allam
March 24, 2023 - In Golang, string are in Go, concatenating strings is fairly easy and straight forward, you simply use the add or addition symbol and you are all set.