Question

How to Assign a String to a Byte Array in Go

Question

var arr [20]byte
str := "abc"
for k, v := range []byte(str) {
    arr[k] = byte(v)
}

How can I assign a string to a fixed-size byte array in Go? Is there a simpler or more idiomatic way than looping over the bytes manually?

Short Answer

By the end of this page, you will understand how strings and byte arrays relate in Go, how to copy string data into a fixed-size [N]byte array, when a loop is fine, and when the built-in copy function is the better choice.

Concept

In Go, a string and a fixed-size byte array like [20]byte are different types with different purposes.

  • A string is an immutable sequence of bytes.
  • A [20]byte is a fixed-size array that stores exactly 20 bytes.

Because they are different types, you cannot directly assign a string to a byte array.

var arr [20]byte
str := "abc"

// Not allowed
// arr = str

Instead, you need to copy the string's bytes into the array.

A common and idiomatic way is to use Go's built-in copy function:

var arr [20]byte
str := "abc"
copy(arr[:], str)

This works because:

  • arr[:] creates a slice view over the whole array.
  • copy can copy from a string into a byte slice.

This concept matters because fixed-size byte arrays are often used in:

  • binary protocols
  • file headers
  • network packets
  • C interop
  • low-level storage formats

In these cases, you often need to place text into a fixed-size field safely and efficiently.

Mental Model

Think of a string as a label containing text, and a [20]byte array as a row of 20 storage boxes.

  • The string "abc" contains 3 bytes.
  • The array has 20 slots.
  • Copying means putting a, b, and c into the first 3 boxes.
  • The remaining 17 boxes stay as zero values.

So this:

copy(arr[:], str)

is like saying: take the bytes from the string and place them into the available boxes until you run out of text or space.

Take Quiz

Syntax and Examples

Basic syntax

var arr [20]byte
str := "abc"
copy(arr[:], str)

What this does

  • arr[:] turns the array into a slice.
  • copy copies bytes from str into that slice.
  • Since str is shorter than 20 bytes, only the first 3 positions are filled.
  • The rest remain 0.

Example: print the array

package main

import "fmt"

func main() {
    var arr [20]byte
    str := "abc"

    copy(arr[:], str)

    fmt.Println(arr)
}

Output:

[97 98 99 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]

Example: convert part of the array back to a string

package main

import "fmt"

func main() {
    var arr [20]byte
    str := "abc"

    copy(arr[:], str)

    fmt.Println(string(arr[:3]))
}

Output:

abc

Manual loop version

Your original approach also works:

var arr [20]byte
str := "abc"
for k, v := range []byte(str) {
    arr[k] = v
}

This is valid, but copy(arr[:], str) is shorter and more idiomatic when you simply want to copy bytes.

Step by Step Execution

Consider this code:

package main

import "fmt"

func main() {
    var arr [5]byte
    str := "hey"

    n := copy(arr[:], str)

    fmt.Println("copied:", n)
    fmt.Println("array:", arr)
    fmt.Println("first 3 bytes as string:", string(arr[:3]))
}

Step-by-step

  1. var arr [5]byte

    • Creates a fixed-size array with 5 bytes.
    • Initial value is:
      [0 0 0 0 0]
      
  2. str := "hey"

    • Creates a string containing 3 bytes: h, e, y.
  3. n := copy(arr[:], str)

    • arr[:] creates a slice covering the full array.
    • copy copies as many bytes as possible.
    • Here, 3 bytes are copied.
    • n becomes 3.
    • Array becomes:
      [104 101 121 0 0]
      
  4. fmt.Println("copied:", n)

    • Prints how many bytes were copied.
  5. fmt.Println("array:", arr)

    • Prints the numeric byte values.
  6. fmt.Println("first 3 bytes as string:", string(arr[:3]))

    • Converts only the meaningful part back into a string.
    • Output is hey.

Important behavior

If the string is longer than the array, copy truncates it:

var arr [4]byte
str := "golang"
copy(arr[:], str)
fmt.Println(string(arr[:]))

Output:

gola

Only the first 4 bytes fit.

Real World Use Cases

Fixed-width file formats

Some binary or legacy file formats store text in fields of a fixed size, such as 16 or 32 bytes.

var name [16]byte
copy(name[:], "alice")

Network protocol headers

Protocols may define fixed-size byte fields for IDs, names, or command codes.

var header [8]byte
copy(header[:], "PING")

Interfacing with C libraries

When working with C-style structs or memory layouts, you may need fixed byte arrays instead of Go strings.

Embedded or low-level programming

Hardware protocols and packed structures often use fixed-length byte storage.

Database or serialization formats

Some systems represent short text fields as fixed-length byte blocks for predictable layout.

Take Quiz

Real Codebase Usage

In real Go codebases, developers commonly use copy for this task because it is simple and safe.

Common pattern: copy into fixed buffer

var buf [32]byte
copy(buf[:], value)

Common pattern: validate length first

If truncation is not acceptable, check first:

if len(str) > len(arr) {
    return fmt.Errorf("string too long")
}
copy(arr[:], str)

This is a good example of a guard clause.

Common pattern: zero-padded fields

A fixed array is automatically zero-filled, so copying a shorter string leaves trailing zero bytes.

var field [10]byte
copy(field[:], "id")

This is useful when formats expect null-padded or zero-padded fields.

Common pattern: helper function

In larger projects, developers often wrap this logic in a helper:

func fillBytes(dst []byte, s string) int {
    return copy(dst, s)
}

This keeps encoding logic consistent across the codebase.

Common Mistakes

Mistake 1: trying direct assignment

var arr [20]byte
str := "abc"
// arr = str // invalid

Why it fails

A string is not the same type as a byte array.

Fix

copy(arr[:], str)

Mistake 2: forgetting that arrays and slices are different

copy needs a slice as the destination, not the array value directly.

var arr [20]byte
str := "abc"
// copy(arr, str) // invalid

Fix

copy(arr[:], str)

Mistake 3: ignoring truncation

var arr [3]byte
copy(arr[:], "hello")
fmt.Println(string(arr[:]))

Output:

hel

Why it matters

copy silently copies only what fits.

Fix

Check length when truncation is not allowed:

if len("hello") > len(arr) {
    panic("too long")
}
copy(arr[:], "hello")

Mistake 4: assuming character count equals byte count

For ASCII text, one character is usually one byte. But with UTF-8 text, some characters use multiple bytes.

str := "é"
fmt.Println(len(str)) // 2

If your array size is based on bytes, this matters.

Mistake 5: converting the entire array back to string without trimming zeros

var arr [5]byte
copy(arr[:], "hi")
fmt.Println(string(arr[:]))

This may include zero bytes at the end.

Better approach

Track the copied length:

n := copy(arr[:], "hi")
fmt.Println(string(arr[:n]))

Comparisons

ApproachExampleBest forNotes
Manual loopfor i, b := range []byte(str) { arr[i] = b }Learning or custom logicMore verbose
copycopy(arr[:], str)Simple byte copyingIdiomatic and concise
Conversion to slice firstcopy(arr[:], []byte(str))When you already need a byte sliceUsually unnecessary for plain copying

copy(arr[:], str) vs looping

  • copy is shorter.
  • copy clearly expresses intent.
  • A loop is better only if you need custom behavior, such as:
    • stopping at a delimiter
    • transforming bytes
    • skipping some characters

Array vs slice

TypeExampleSizeResizableCommon use
Array[20]byteFixedNoFixed-layout data
Slice[]byteDynamic viewYes, with appendGeneral-purpose byte handling

If you do not need a fixed size, a slice is often easier to work with.

b := []byte("abc")
Take Quiz

Cheat Sheet

// Copy string into fixed byte array
var arr [20]byte
copy(arr[:], "abc")

// Get number of bytes copied
n := copy(arr[:], "abc")

// Prevent truncation
if len(str) > len(arr) {
    return
}
copy(arr[:], str)

// Convert back using only meaningful bytes
s := string(arr[:n])

Rules

  • You cannot assign a string directly to a [N]byte array.
  • Use arr[:] to make a slice from the array.
  • Use copy(arr[:], str) for idiomatic copying.
  • copy truncates if the source is too long.
  • Unused array elements remain 0.
  • len(str) returns bytes, not necessarily characters.

Good default pattern

var arr [20]byte
if len(str) > len(arr) {
    return
}
copy(arr[:], str)

FAQ

Can I assign a Go string directly to a byte array?

No. A string and a fixed-size array like [20]byte are different types.

What is the simplest way to copy a string into [N]byte in Go?

Use:

copy(arr[:], str)

Does copy work directly from a string?

Yes. In Go, copy can copy from a string into a byte slice.

What happens if the string is longer than the array?

Only the bytes that fit are copied. The rest are ignored.

Why use arr[:] instead of arr?

Because copy needs a slice destination, and arr[:] creates a slice over the array.

Is a manual loop wrong?

No. It works fine, but copy is usually cleaner and more idiomatic.

Are strings always one byte per character in Go?

No. Go strings are UTF-8 encoded, so some characters use multiple bytes.

When should I use []byte instead of [N]byte?

Use []byte for flexible, general-purpose byte data. Use [N]byte when you need a fixed-size layout.

Take Quiz

Mini Project

Description

Build a small Go program that stores short command names in fixed-size byte arrays. This mirrors real situations like binary file formats, packet headers, or fixed-width records where a text field must fit into a specific number of bytes.

Goal

Create a program that copies strings into fixed-size byte arrays, detects overly long input, and prints the stored result safely.

Requirements

  • Create a helper function that copies a string into a [8]byte field.
  • Reject strings that are longer than 8 bytes.
  • Return how many bytes were copied.
  • Print both the raw byte array and the reconstructed string.
  • Test the program with one valid string and one too-long string.
Take Quiz

Keep learning

Related questions

View more