A Go string holds read-only bytes (usually UTF-8 text). A []byte is mutable. Converting go bytes to string is common for I/O, HTTP bodies, and protocols. For turning strings or readers into byte views, see byte slice to io.Reader. For concurrency basics when sharing data, see goroutines.
Tested with Go 1.24 on Linux.
Direct conversion: string(b)
The zero-cost pattern for valid UTF-8 (or treating bytes as opaque) is s := string(b).
package main
import "fmt"
func main() {
hello := []byte("Hello")
fmt.Println(string(hello))
empty := []byte{}
fmt.Printf("empty len=%d\n", len(string(empty)))
}You should see Hello and empty len=0.
Formatting with fmt.Sprintf
Use fmt.Sprintf when the byte slice is part of a larger formatted message.
package main
import "fmt"
func main() {
b := []byte("Go")
fmt.Printf("%s\n", b)
fmt.Println(fmt.Sprintf("Hello, %s!", b))
}strings.Builder and bytes.Buffer
Use strings.Builder when you append many fragments and then call String(). Use bytes.Buffer when you already mix binary reads and writes.
package main
import (
"bytes"
"fmt"
"strings"
)
func main() {
var sb strings.Builder
sb.Write([]byte("Hello"))
sb.WriteByte(',')
sb.Write([]byte(" world"))
fmt.Println(sb.String())
var buf bytes.Buffer
buf.WriteString("bytes.Buffer")
fmt.Println(buf.String())
}Numbers: strconv instead of casting
If bytes hold decimal digits, use strconv.ParseInt or Atoi—do not use string(b) for numeric parsing.
package main
import (
"fmt"
"strconv"
)
func main() {
n, err := strconv.Atoi(string([]byte("42")))
if err != nil {
panic(err)
}
fmt.Println(n + 1)
}You should see 43.
unsafe (not recommended)
The unsafe package can force a reinterpretation of memory. It is rarely justified for []byte→string in application code: it breaks invariants if the slice mutates, and future compiler changes can make subtle bugs. Prefer string(b) unless a profiler shows a proven hotspot and you follow the unsafe documentation carefully.
Summary
For go bytes to string, start with string(b) for simple UTF-8 or opaque bytes. Use fmt.Sprintf for formatted output, strings.Builder or bytes.Buffer for incremental assembly, and strconv when the bytes represent encoded numbers. Avoid unsafe unless you have a measured need and strong review. Use io.ReadAll (not deprecated ioutil) when reading from an io.Reader.
References
- Package strings
- Package bytes
- Convert slice to string (Go wiki)
Frequently Asked Questions
How do I convert []byte to string in Go?
Use string(b) for a standard conversion that validates UTF-8 for string operations and may share the backing array with the slice per compiler rules; treat the string as read-only.
Does string([]byte) copy the bytes?
The language may reuse the backing array when safe; you should not mutate the byte slice after conversion if you rely on the string contents staying fixed. For independent copies, allocate a new string by building with strings.Builder or append to a fresh slice first.
When should I use strings.Builder instead of string(b)?
When you assemble many pieces in a loop and want fewer allocations; call Write or WriteByte/WriteRune then String().
Is unsafe.Pointer for []byte to string recommended?
Almost never. It bypasses type safety and can break if the slice is modified while the string is used. Prefer string(b) or copying patterns documented in the unsafe package.
What replaced ioutil.ReadAll for reading into []byte?
Use io.ReadAll from the io package since Go 1.16; then convert the resulting []byte with string(data) if you need text.

