When you work with sockets, files, or protocols, you need to choose the right meaning of “bytes”: a single uint8, UTF-8 text digits for strconv, fixed-width wire integers for encoding/binary, or a long unsigned magnitude for math/big. The numbered examples below mirror the usual teaching order (ASCII first, then binary helpers, then reverses). For text bytes generally, see Go bytes to string. For broader math APIs, see Golang math.
Tested with Go 1.24 on Linux.
Single byte / uint8 to int (not []byte)
A byte is an alias for uint8. Widening to int is a conversion:
package main
import "fmt"
func main() {
var b byte = 200
var u uint8 = 255
fmt.Println(int(b), int(u))
}You should see 200 and 255.
Example 1: Convert ASCII []byte to int
If the slice holds only decimal digits (and fits int range), strconv.Atoi is the shortest path. The argument must be valid UTF-8; ASCII digits are fine.
package main
import (
"fmt"
"strconv"
)
func main() {
bs := []byte{'6', '6', '1'}
n, err := strconv.Atoi(string(bs))
if err != nil {
fmt.Println(err)
return
}
fmt.Println(n)
}You should see 661.
Example 2: Convert ASCII []byte to an integer with validation
Atoi is convenient but does not let you cap bit width. Prefer strconv.ParseInt when you need a maximum size (for example values that must fit int32), want an explicit base, or must trim spaces and reject garbage.
package main
import (
"fmt"
"strconv"
"strings"
)
func parseDecimalInt32(bs []byte) (int32, error) {
s := strings.TrimSpace(string(bs))
v, err := strconv.ParseInt(s, 10, 32)
if err != nil {
return 0, err
}
return int32(v), nil
}
func main() {
for _, bs := range [][]byte{
[]byte(" -42 "),
[]byte("9999999999"), // overflows int32
[]byte("12a"),
} {
v, err := parseDecimalInt32(bs)
if err != nil {
fmt.Printf("%q -> %v\n", string(bs), err)
continue
}
fmt.Printf("%q -> %d\n", string(bs), v)
}
}You should see -42 parse successfully, then strconv range or syntax errors for the bad rows.
For unsigned decimal text, use strconv.ParseUint with an appropriate bit size instead of ParseInt.
Example 3: Using math/big to convert []byte to an integer
Int.SetBytes interprets the slice as an unsigned big-endian integer of arbitrary length. It does not read a sign bit from the bytes; treat it as a magnitude.
package main
import (
"fmt"
"math/big"
)
func main() {
bs := []byte{0x01, 0x00, 0x01}
i := new(big.Int).SetBytes(bs)
fmt.Println(i.String())
}You should see 65537.
Example 4: Using encoding/binary to convert a byte slice to int32 (or uint32)
When you already have exactly four bytes in wire order, you can decode without a bytes.Reader using Uint32 on the correct endianness, then cast if the protocol uses signed int32 with two’s-complement bits.
package main
import (
"encoding/binary"
"fmt"
)
func main() {
raw := []byte{0x01, 0xFF, 0x01, 0xFF}
if len(raw) < 4 {
fmt.Println("too short")
return
}
u := binary.BigEndian.Uint32(raw[:4])
fmt.Println("as uint32:", u)
fmt.Println("as int32:", int32(u))
le := []byte{0x04, 0x03, 0x02, 0x01}
fmt.Println("little-endian uint32:", binary.LittleEndian.Uint32(le[:4]))
}You should see 33489407 for the big-endian uint32, the same bit pattern reinterpreted as int32, and 16909060 for the little-endian example.
For eight-byte fields, use Uint64 and int64(binary.BigEndian.Uint64(buf)) when the wire format is signed two’s complement.
Variant: binary.Read for int32 from a stream
When bytes come from an io.Reader or you want the decoder to advance a cursor, binary.Read into a typed variable is convenient:
package main
import (
"bytes"
"encoding/binary"
"fmt"
)
func main() {
var v int32
raw := []byte{0x01, 0xFF, 0x01, 0xFF}
r := bytes.NewReader(raw)
if err := binary.Read(r, binary.BigEndian, &v); err != nil {
fmt.Println(err)
return
}
fmt.Println(v)
}You should see 33489407 as an int32 (same bits as the Uint32 example).
Example 5: Using encoding/binary to convert int64 to []byte (and back)
For signed int64 on the wire, the usual pattern is to cast to uint64 on write and cast back to int64 on read so two’s-complement layout is preserved.
package main
import (
"encoding/binary"
"fmt"
)
func main() {
var x int64 = -1
buf := make([]byte, 8)
binary.BigEndian.PutUint64(buf, uint64(x))
fmt.Println(buf)
round := int64(binary.BigEndian.Uint64(buf))
fmt.Println(round)
}You should see eight non-zero bytes for -1 in big-endian form, then -1 after the round trip.
Go 1.19+ also offers binary.Append to append a primitive to an existing []byte without pre-sizing manually.
Example 6: Decimal text int to []byte (reverse of Examples 1–2)
Use strconv.FormatInt (or FormatUint) and convert the string to bytes:
package main
import (
"fmt"
"strconv"
)
func main() {
n := int64(33489407)
ascii := strconv.FormatInt(n, 10)
fmt.Println([]byte(ascii))
}You should see ASCII bytes for 33489407.
Summary
Pick the conversion that matches the data: int(b) for one byte; Examples 1–2 for decimal ASCII; Example 3 for long unsigned big-endian magnitudes; Examples 4–5 for fixed-width binary with explicit endianness and signed int64 casts when needed; Example 6 for human-readable decimal bytes. Always check lengths before slicing buf[:4] or buf[:8], and always handle strconv errors so 0 is not ambiguous.

