Question

How to Join a Slice of Strings into One String in Go

goslice

Question

I am trying to combine multiple strings into one comma-separated string in Go using strings.Join, but my code gives a type error:

package main

import (
    "fmt"
    "strings"
)

func main() {
    reg := [...]string{"a", "b", "c"}
    fmt.Println(strings.Join(reg, ","))
}

The error is:

prog.go:10: cannot use reg (type [3]string) as type []string in argument to strings.Join

Why does this happen, and what is the correct way to join these values into a single string? Is there a better approach than manually looping and appending to a variable?

Short Answer

By the end of this page, you will understand why strings.Join accepts a slice of strings rather than an array, how arrays and slices differ in Go, and how to correctly convert or declare your data so it can be joined. You will also see practical examples, common mistakes, and how this appears in real Go codebases.

Concept

In Go, strings.Join is designed to work with a slice of strings:

func Join(elems []string, sep string) string

That means its first argument must be of type []string.

In your code, however, reg is declared as:

reg := [...]string{"a", "b", "c"}

This creates an array, not a slice. In Go, arrays and slices are related, but they are different types.

  • [3]string means: an array with exactly 3 strings
  • []string means: a slice of strings of any length

Even though they look similar, Go does not automatically treat an array as a slice when calling a function that expects []string.

To use strings.Join, you have two common options:

  1. Declare the value as a slice from the start
  2. Slice the array using reg[:]

This matters in real programming because many Go APIs use slices instead of arrays. Slices are more flexible, easier to pass around, and the standard library uses them heavily.

Mental Model

Think of an array as a fixed-size storage box with a label saying exactly how many compartments it has.

  • [3]string = a box with exactly 3 slots
  • [5]string = a different box with 5 slots

A slice is more like a view or window into a sequence of items.

  • []string = “some strings,” without fixing the count in the type

strings.Join says: “Give me a slice view of strings.”

If you hand it a fixed-size array box, Go says: “That is not the same type.”

When you write reg[:], you are saying: “Create a slice view over the whole array.” That gives strings.Join exactly what it expects.

Take Quiz

Syntax and Examples

The correct syntax for strings.Join is:

strings.Join(sliceOfStrings, separator)

Example 1: Declare a slice directly

package main

import (
    "fmt"
    "strings"
)

func main() {
    reg := []string{"a", "b", "c"}
    result := strings.Join(reg, ",")
    fmt.Println(result)
}

Output:

a,b,c

This is the simplest approach when you want to join values.

Example 2: Convert an array to a slice

package main

import (
    "fmt"
    "strings"
)

func main() {
    reg := [...]string{"a", "b", "c"}
    result := strings.Join(reg[:], ",")
    fmt.Println(result)
}

Output:

a,b,c

Here, reg[:] creates a slice containing the entire array.

Example 3: Different separator

package main

import (
    "fmt"
    "strings"
)

func main() {
    words := []string{"Go", "is", "fast"}
    fmt.Println(strings.Join(words, " "))
}

Output:

Go is fast

Example 4: Empty slice

package main

import (
    "fmt"
    "strings"
)

func main() {
    var items []string
    fmt.Println(strings.Join(items, ","))
}

Output:

Joining an empty slice returns an empty string.

Step by Step Execution

Consider this example:

package main

import (
    "fmt"
    "strings"
)

func main() {
    reg := [...]string{"a", "b", "c"}
    result := strings.Join(reg[:], ",")
    fmt.Println(result)
}

Step by step:

  1. reg := [...]string{"a", "b", "c"}

    • Go creates an array of type [3]string
    • It contains three values: "a", "b", and "c"
  2. reg[:]

    • This creates a slice over the entire array
    • The new value has type []string
  3. strings.Join(reg[:], ",")

    • strings.Join receives the slice
    • It places "," between each element
    • It builds the final string: "a,b,c"
  4. fmt.Println(result)

    • Prints:
a,b,c

Quick trace

ExpressionTypeValue
reg[3]string{"a", "b", "c"}
reg[:][]string{"a", "b", "c"}
strings.Join(reg[:], ",")string"a,b,c"

Real World Use Cases

Joining string slices is common in many kinds of Go programs.

Building CSV-like output

row := []string{"Alice", "25", "London"}
line := strings.Join(row, ",")

Creating log messages

parts := []string{"INFO", "server started", "port=8080"}
msg := strings.Join(parts, " | ")

Generating file paths or keys

segments := []string{"users", "42", "profile"}
path := strings.Join(segments, "/")

Formatting tags or labels

tags := []string{"go", "backend", "api"}
output := strings.Join(tags, ", ")

Combining SQL placeholders or dynamic content

When building text from known safe string parts, strings.Join is cleaner and more readable than repeated concatenation.

Take Quiz

Real Codebase Usage

In real Go projects, developers usually prefer slices over arrays for data they want to pass to functions.

Common pattern: declare slices directly

headers := []string{"Content-Type", "Authorization", "X-Trace-ID"}
fmt.Println(strings.Join(headers, ", "))

This is common because slices work naturally with:

  • function parameters
  • loops
  • append
  • standard library functions

Pattern: join after collecting values

var errors []string
errors = append(errors, "name is required")
errors = append(errors, "email is invalid")
msg := strings.Join(errors, "; ")

This is often used in validation and error reporting.

Pattern: guard clause for empty data

func formatTags(tags []string) string {
    if len(tags) == 0 {
        return "no tags"
    }
    return strings.Join(tags, ", ")
}

Pattern: build parts first, then join

Instead of manually adding separators in a loop, developers often do this:

parts := []string{}
if true {
    parts = append(parts, "name=alice")
}
if true {
    parts = append(parts, "active=true")
}
query := strings.Join(parts, "&")

This avoids tricky separator logic like “add a comma except after the last item.”

Common Mistakes

1. Using an array instead of a slice

Broken code:

reg := [...]string{"a", "b", "c"}
fmt.Println(strings.Join(reg, ","))

Why it fails:

  • reg is an array
  • strings.Join expects []string

Fix:

fmt.Println(strings.Join(reg[:], ","))

Or declare it as a slice:

reg := []string{"a", "b", "c"}

2. Confusing [3]string and []string

These are not interchangeable.

  • [3]string and [4]string are also different from each other
  • []string is a separate type

3. Manually concatenating in a loop

Beginners often write:

result := ""
for i, s := range reg {
    if i > 0 {
        result += ","
    }
    result += s
}

This works, but strings.Join is simpler and clearer when you already have a slice of strings.

4. Expecting automatic conversion

Go does not automatically convert many types, even when the values look compatible.

Broken assumption:

// Go will not convert this automatically
arr := [...]string{"x", "y"}
_ = strings.Join(arr, "-")

Fix:

_ = strings.Join(arr[:], "-")

5. Forgetting that separators go between items

items := []string{"a", "b", "c"}
fmt.Println(strings.Join(items, ","))

Output:

a,b,c

The separator is placed between elements, not at the beginning or end.

Comparisons

ConceptTypeFixed size?Common for function parameters?Works with strings.Join directly?
Array[3]stringYesNoNo
Slice[]stringNoYesYes

Array vs slice in Go

arr := [...]string{"a", "b", "c"} // array
slc := []string{"a", "b", "c"}    // slice

Use an array when:

  • the fixed length is part of the data itself
  • you explicitly want a value with a known size in its type

Use a slice when:

  • you want to pass data to functions
  • you want flexibility
  • you need to use most standard library APIs

strings.Join vs manual concatenation

ApproachBest forProsCons
strings.JoinJoining existing []string valuesClear, idiomatic, simpleRequires a slice
+ concatenationA few string piecesEasy for very small casesGets messy in loops
Manual loopCustom formatting logicFlexibleMore code, easier to make mistakes
Take Quiz

Cheat Sheet

// strings.Join signature
strings.Join(elems []string, sep string) string

Use a slice directly

items := []string{"a", "b", "c"}
result := strings.Join(items, ",")

Convert an array to a slice

arr := [...]string{"a", "b", "c"}
result := strings.Join(arr[:], ",")

Key rules

  • strings.Join needs []string
  • [3]string is an array, not a slice
  • arr[:] creates a slice over the whole array
  • Empty slice joined with any separator returns ""
  • Separator is inserted between elements only

Common forms

strings.Join(words, " ")   // sentence
strings.Join(parts, "/")   // path-like output
strings.Join(tags, ", ")   // comma-separated list
strings.Join(lines, "\n")  // multiline string

Quick fix for the original error

reg := [...]string{"a", "b", "c"}
fmt.Println(strings.Join(reg[:], ","))

Or better:

reg := []string{"a", "b", "c"}
fmt.Println(strings.Join(reg, ","))

FAQ

Why does strings.Join not accept an array in Go?

Because its function signature explicitly requires []string, which is a slice type. Arrays and slices are different types in Go.

How do I convert an array to a slice in Go?

Use slicing syntax:

arr[:] 

This creates a slice covering the whole array.

Should I use arrays or slices for strings in Go?

In most everyday Go code, use slices. Arrays are less common for function arguments because their size is part of the type.

Is strings.Join better than looping manually?

Yes, when you already have a slice of strings. It is shorter, clearer, and idiomatic.

What happens if I join an empty slice?

You get an empty string:

strings.Join([]string{}, ",") // ""

Can I join values that are not strings?

Not directly with strings.Join. You must first convert them to strings.

Does strings.Join add a separator at the end?

No. The separator is only placed between elements.

Take Quiz

Mini Project

Description

Create a small Go program that formats a list of command-line style labels into a single readable string. This demonstrates how slices are collected and joined using strings.Join, which is a common pattern in logging, reporting, and configuration output.

Goal

Build a Go program that joins a list of labels into a comma-separated string and handles the empty case cleanly.

Requirements

  • Declare a slice of strings containing at least three labels.
  • Use strings.Join to combine them into one string.
  • Print the final result.
  • Add a small function that returns a fallback message when the slice is empty.
Take Quiz

Keep learning

Related questions

View more