Bytes #

Package bytes adalah cermin dari package strings — hampir semua fungsi yang ada di strings punya padanannya di bytes, tapi bekerja pada []byte alih-alih string. Ini penting karena di Go, konversi antara string dan []byte selalu menghasilkan salinan baru di memori. Kode yang banyak melakukan konversi bolak-balik []bytestring akan membebani garbage collector. Package bytes memungkinkan kamu bekerja langsung dengan []byte tanpa konversi yang tidak perlu — terutama penting saat memproses data dari jaringan, file, atau stream yang secara natural berbentuk byte. Di samping fungsi manipulasi, bytes.Buffer adalah tool yang sangat sering dipakai untuk membangun []byte atau string secara inkremental dengan efisien.

Gambaran Besar Package bytes #

flowchart TD
    B["package bytes"] --> Func["Fungsi Manipulasi\n(mirip strings)"]
    B --> Buffer["bytes.Buffer\nbangun data inkremental"]
    B --> Reader["bytes.Reader\nio.Reader dari []byte"]

    Func --> Search["Pencarian\nContains / Index / Count\nIndexByte / IndexRune"]
    Func --> Transform["Transformasi\nToUpper / ToLower / Title\nTrimSpace / Trim / Replace"]
    Func --> Split["Pemisahan & Gabungan\nSplit / SplitN / Fields\nJoin / Repeat"]
    Func --> Compare["Perbandingan\nEqual / Compare\nHasPrefix / HasSuffix"]

    Buffer --> BW["io.Writer\nWrite / WriteByte / WriteString\nWriteRune"]
    Buffer --> BR["io.Reader\nRead / ReadByte / ReadRune\nReadString / ReadLine"]
    Buffer --> BA["Akses\nBytes() / String()\nLen() / Cap() / Reset()"]

    Reader --> RR["io.Reader, io.Seeker\nio.ReaderAt\nSeek / ReadAt"]

    style B fill:#4f86c6,color:#fff
    style Func fill:#e8f5e9
    style Buffer fill:#e3f2fd
    style Reader fill:#fff3e0

Fungsi Dasar — Pencarian dan Pemeriksaan #

package main

import (
    "bytes"
    "fmt"
)

func main() {
    data := []byte("Halo, Dunia! Halo, Go!")

    // Contains — apakah mengandung sub-slice?
    fmt.Println(bytes.Contains(data, []byte("Dunia")))  // true
    fmt.Println(bytes.Contains(data, []byte("Python"))) // false

    // ContainsAny — mengandung byte apapun dari string?
    fmt.Println(bytes.ContainsAny(data, "aeiou")) // true — ada vokal
    fmt.Println(bytes.ContainsAny(data, "xyz"))   // false

    // ContainsRune — mengandung rune tertentu?
    fmt.Println(bytes.ContainsRune(data, '!')) // true

    // Count — hitung kemunculan
    fmt.Println(bytes.Count(data, []byte("Halo"))) // 2
    fmt.Println(bytes.Count(data, []byte("")))      // 23 (len+1)

    // Index — posisi kemunculan pertama (-1 jika tidak ada)
    fmt.Println(bytes.Index(data, []byte("Dunia"))) // 6
    fmt.Println(bytes.Index(data, []byte("Java")))  // -1

    // LastIndex — posisi kemunculan terakhir
    fmt.Println(bytes.LastIndex(data, []byte("Halo"))) // 14

    // IndexByte — cari satu byte (lebih cepat dari Index)
    fmt.Println(bytes.IndexByte(data, '!')) // 12

    // IndexRune — cari satu rune (mendukung multibyte)
    fmt.Println(bytes.IndexRune(data, 'D')) // 6

    // IndexAny — cari byte apapun dari set
    fmt.Println(bytes.IndexAny(data, "aeiou")) // 1 — 'a' di "Halo"

    // HasPrefix dan HasSuffix
    fmt.Println(bytes.HasPrefix(data, []byte("Halo"))) // true
    fmt.Println(bytes.HasSuffix(data, []byte("Go!")))  // true
}

Transformasi — Mengubah Isi Byte Slice #

data := []byte("  Halo, Dunia!  ")

// Trim — hapus karakter dari kedua ujung
fmt.Println(string(bytes.TrimSpace(data)))              // "Halo, Dunia!"
fmt.Println(string(bytes.Trim(data, " !")))             // "Halo, Dunia"
fmt.Println(string(bytes.TrimLeft(data, " ")))          // "Halo, Dunia!  "
fmt.Println(string(bytes.TrimRight(data, " ")))         // "  Halo, Dunia!"
fmt.Println(string(bytes.TrimPrefix(data, []byte("  ")))) // "Halo, Dunia!  "
fmt.Println(string(bytes.TrimSuffix(data, []byte("  ")))) // "  Halo, Dunia!"

// TrimFunc — hapus byte yang memenuhi kondisi
clean := bytes.TrimFunc(data, func(r rune) bool {
    return r == ' ' || r == '!'
})
fmt.Println(string(clean)) // "Halo, Dunia"

// Case conversion
s := []byte("halo dunia")
fmt.Println(string(bytes.ToUpper(s))) // "HALO DUNIA"
fmt.Println(string(bytes.ToLower([]byte("HALO DUNIA")))) // "halo dunia"
fmt.Println(string(bytes.ToTitle(s))) // "HALO DUNIA" (title = upper untuk ASCII)

// Title — kapitalkan awal kata (deprecated di strings, tapi ada di bytes)
fmt.Println(string(bytes.Title(s))) // "Halo Dunia"

// Replace dan ReplaceAll
teks := []byte("kucing makan ikan, kucing senang")
fmt.Println(string(bytes.Replace(teks, []byte("kucing"), []byte("anjing"), 1)))
// "anjing makan ikan, kucing senang" — ganti hanya pertama

fmt.Println(string(bytes.ReplaceAll(teks, []byte("kucing"), []byte("anjing"))))
// "anjing makan ikan, anjing senang" — ganti semua

// Map — transformasi per rune
hasil := bytes.Map(func(r rune) rune {
    if r >= 'a' && r <= 'z' {
        return r - 32 // lowercase ke uppercase
    }
    return r
}, []byte("halo, dunia!"))
fmt.Println(string(hasil)) // "HALO, DUNIA!"

Pemisahan dan Penggabungan #

// Split — pisahkan dengan separator
data := []byte("apel,mangga,jeruk,pisang")
bagian := bytes.Split(data, []byte(","))
for _, b := range bagian {
    fmt.Println(string(b))
}
// apel
// mangga
// jeruk
// pisang

// SplitN — maksimal N bagian
bagian2 := bytes.SplitN(data, []byte(","), 2)
fmt.Println(string(bagian2[0])) // "apel"
fmt.Println(string(bagian2[1])) // "mangga,jeruk,pisang"

// SplitAfter — separator ikut di bagian sebelumnya
bagian3 := bytes.SplitAfter(data, []byte(","))
// ["apel," "mangga," "jeruk," "pisang"]

// Fields — pisahkan berdasarkan whitespace (mirip strings.Fields)
kalimat := []byte("  halo   dunia   go  ")
kata := bytes.Fields(kalimat)
for _, k := range kata {
    fmt.Printf("[%s]\n", k)
}
// [halo]
// [dunia]
// [go]

// FieldsFunc — pisahkan berdasarkan kondisi
csv := []byte("satu,,dua,,,tiga")
kolom := bytes.FieldsFunc(csv, func(r rune) bool {
    return r == ','
})
// ["satu" "dua" "tiga"] — field kosong dilewati

// Join — gabungkan dengan separator
buah := [][]byte{[]byte("apel"), []byte("mangga"), []byte("jeruk")}
hasil := bytes.Join(buah, []byte(", "))
fmt.Println(string(hasil)) // "apel, mangga, jeruk"

// Repeat — ulangi byte slice
fmt.Println(string(bytes.Repeat([]byte("ab"), 4))) // "abababab"
fmt.Println(string(bytes.Repeat([]byte("-"), 20)))  // "--------------------"

Perbandingan Byte Slice #

a := []byte("apel")
b := []byte("mangga")
c := []byte("apel")

// Equal — apakah isinya sama?
fmt.Println(bytes.Equal(a, c)) // true
fmt.Println(bytes.Equal(a, b)) // false

// ANTI-PATTERN: bandingkan dengan string()
// Ini membuat alokasi salinan string!
fmt.Println(string(a) == string(c)) // true tapi tidak efisien

// BENAR: gunakan bytes.Equal
fmt.Println(bytes.Equal(a, c)) // true tanpa alokasi

// Compare — seperti strcmp: -1, 0, atau 1
fmt.Println(bytes.Compare(a, b)) // -1 (apel < mangga)
fmt.Println(bytes.Compare(b, a)) // 1  (mangga > apel)
fmt.Println(bytes.Compare(a, c)) // 0  (sama)

// EqualFold — bandingkan case-insensitive
x := []byte("Halo")
y := []byte("halo")
fmt.Println(bytes.EqualFold(x, y)) // true

bytes.Buffer — Membangun Data Secara Inkremental #

bytes.Buffer adalah tool yang paling sering dipakai dari package bytes. Ia mengimplementasikan io.Reader dan io.Writer, membuatnya sangat fleksibel untuk membangun data byte secara inkremental tanpa banyak alokasi.

flowchart LR
    subgraph Write["Menulis ke Buffer"]
        W1["Write([]byte)\ntulis byte slice"]
        W2["WriteByte(byte)\ntulis satu byte"]
        W3["WriteString(string)\ntulis string"]
        W4["WriteRune(rune)\ntulis satu rune"]
        W5["fmt.Fprintf(&buf, ...)\ntulis dengan format"]
    end

    subgraph Buffer["bytes.Buffer"]
        B["internal\n[]byte"]
    end

    subgraph Read["Membaca dari Buffer"]
        R1["Read([]byte)\nbaca ke slice"]
        R2["ReadByte()\nbaca satu byte"]
        R3["ReadRune()\nbaca satu rune"]
        R4["ReadString('\\n')\nbaca sampai delimiter"]
        R5["ReadLine()\nbaca satu baris"]
    end

    subgraph Access["Akses Data"]
        A1["Bytes() []byte\nisi buffer (tidak salin)"]
        A2["String() string\nisi sebagai string"]
        A3["Len() int\njumlah byte tersisa"]
        A4["Reset()\nkosongkan buffer"]
    end

    Write --> Buffer
    Buffer --> Read
    Buffer --> Access
import (
    "bytes"
    "fmt"
)

// Dasar: membangun byte slice
var buf bytes.Buffer

buf.WriteString("Halo, ")
buf.WriteString("Dunia")
buf.WriteByte('!')
buf.WriteRune('🌍')

fmt.Println(buf.String()) // "Halo, Dunia!🌍"
fmt.Println(buf.Len())    // 17 (dalam byte, bukan rune)

// Gunakan fmt.Fprintf untuk format
var buf2 bytes.Buffer
for i := 1; i <= 5; i++ {
    fmt.Fprintf(&buf2, "item %d\n", i)
}
fmt.Print(buf2.String())
// item 1
// item 2
// item 3
// item 4
// item 5

// Reset dan reuse
buf.Reset()
fmt.Println(buf.Len())    // 0
fmt.Println(buf.Cap())    // kapasitas masih ada, tidak direalokasi

// Inisialisasi dengan konten awal
buf3 := bytes.NewBuffer([]byte("data awal"))
buf3.WriteString(" ditambah")
fmt.Println(buf3.String()) // "data awal ditambah"

// Membaca dari buffer
buf4 := bytes.NewBuffer([]byte("baris pertama\nbaris kedua\nbaris ketiga\n"))
line, err := buf4.ReadString('\n')
fmt.Print(line) // "baris pertama\n"
fmt.Println(err) // nil

line, err = buf4.ReadString('\n')
fmt.Print(line) // "baris kedua\n"

Buffer vs strings.Builder — Kapan Mana #

flowchart TD
    Q{"Tujuan output?"} --> S["Hanya butuh string\npada akhirnya"]
    Q --> B["Butuh []byte\natau keduanya"]
    Q --> IO["Butuh io.Reader\natau io.Writer"]

    S --> SB["strings.Builder\nlebih efisien untuk string\ntidak bisa Read"]
    B --> BB["bytes.Buffer\nfleksibel: bisa baca dan tulis\nbisa jadi io.Reader/Writer"]
    IO --> BB2["bytes.Buffer\nimplementasi keduanya"]

    SB --> SE["String() untuk ambil hasil"]
    BB --> BE["Bytes() atau String()\nuntuk ambil hasil"]

    style SB fill:#e8f5e9
    style BB fill:#e3f2fd
    style BB2 fill:#e3f2fd
// strings.Builder — untuk membangun string (tidak bisa dibaca sebagai Reader)
var sb strings.Builder
sb.WriteString("Halo, ")
sb.WriteString("Dunia!")
hasil := sb.String() // ambil hasil sebagai string

// bytes.Buffer — untuk membangun []byte atau saat butuh io.Reader
var buf bytes.Buffer
buf.WriteString("data protokol")
buf.WriteByte(0x00) // bisa tulis byte apapun termasuk null

// Kirim sebagai io.Reader ke fungsi lain
json.NewDecoder(&buf).Decode(&target)
http.Post(url, "application/octet-stream", &buf)

// ANTI-PATTERN: gunakan bytes.Buffer hanya untuk string akhir
var buf2 bytes.Buffer
for i := 0; i < 100; i++ {
    buf2.WriteString("item") // Buffer bisa, tapi Builder lebih efisien
}
_ = buf2.String()

// BENAR: strings.Builder untuk pure string building
var sb2 strings.Builder
for i := 0; i < 100; i++ {
    sb2.WriteString("item")
}
_ = sb2.String()

bytes.Reader — io.Reader dari []byte #

bytes.Reader mengubah []byte menjadi io.Reader yang mendukung seeking — berguna saat kamu punya data di memori tapi fungsi yang akan menerimanya mengharapkan io.Reader:

data := []byte(`{"nama":"Budi","umur":30}`)

// Buat Reader dari []byte
reader := bytes.NewReader(data)

// Decode JSON dari Reader (bukan dari []byte langsung)
var pengguna struct {
    Nama string `json:"nama"`
    Umur int    `json:"umur"`
}
json.NewDecoder(reader).Decode(&pengguna)
fmt.Println(pengguna.Nama, pengguna.Umur) // Budi 30

// Seek — kembali ke posisi tertentu
reader.Seek(0, 0) // kembali ke awal
fmt.Println(reader.Len()) // 25 — kembali ke panjang penuh

// ReadAt — baca dari posisi tertentu tanpa menggeser posisi
buf := make([]byte, 4)
reader.ReadAt(buf, 2)
fmt.Println(string(buf)) // "\"nam"

// Ukuran
fmt.Println(reader.Size()) // 25 — ukuran total (tidak berubah setelah Seek)

// Gunakan sebagai io.Reader untuk HTTP upload
data2 := []byte("konten file ini")
resp, err := http.Post(
    "https://api.example.com/upload",
    "application/octet-stream",
    bytes.NewReader(data2),
)
_ = resp
_ = err

Bekerja dengan Data Biner #

Package bytes sangat berguna saat memproses data biner — protokol jaringan, format file, atau stream data yang berisi campuran teks dan binary:

import (
    "bytes"
    "encoding/binary"
    "fmt"
)

// Parsing frame protokol sederhana:
// [4 byte panjang][1 byte tipe][N byte payload]
func parseFrame(data []byte) (tipe byte, payload []byte, err error) {
    if len(data) < 5 {
        return 0, nil, fmt.Errorf("frame terlalu pendek: %d byte", len(data))
    }

    reader := bytes.NewReader(data)

    // Baca panjang payload (4 byte big-endian)
    var panjang uint32
    if err := binary.Read(reader, binary.BigEndian, &panjang); err != nil {
        return 0, nil, fmt.Errorf("baca panjang: %w", err)
    }

    // Baca tipe (1 byte)
    tipeByte, err := reader.ReadByte()
    if err != nil {
        return 0, nil, fmt.Errorf("baca tipe: %w", err)
    }

    // Baca payload
    payload = make([]byte, panjang)
    if _, err := reader.Read(payload); err != nil {
        return 0, nil, fmt.Errorf("baca payload: %w", err)
    }

    return tipeByte, payload, nil
}

// Membangun frame protokol
func buatFrame(tipe byte, payload []byte) []byte {
    var buf bytes.Buffer

    // Tulis panjang payload (4 byte big-endian)
    binary.Write(&buf, binary.BigEndian, uint32(len(payload)))

    // Tulis tipe
    buf.WriteByte(tipe)

    // Tulis payload
    buf.Write(payload)

    return buf.Bytes()
}

// Penggunaan
frame := buatFrame(0x01, []byte("Halo dari Go!"))
tipe, payload, err := parseFrame(frame)
if err == nil {
    fmt.Printf("Tipe: 0x%02X, Payload: %s\n", tipe, payload)
}

Memproses HTTP Response Body #

import (
    "bytes"
    "compress/gzip"
    "io"
    "net/http"
)

func ambilDanProses(url string) ([]byte, error) {
    resp, err := http.Get(url)
    if err != nil {
        return nil, fmt.Errorf("GET %s: %w", url, err)
    }
    defer resp.Body.Close()

    // Baca seluruh body ke buffer
    var buf bytes.Buffer
    if _, err := io.Copy(&buf, resp.Body); err != nil {
        return nil, fmt.Errorf("baca body: %w", err)
    }

    data := buf.Bytes()

    // Periksa apakah gzip-encoded
    if bytes.HasPrefix(data, []byte{0x1f, 0x8b}) {
        // Magic bytes gzip
        reader, err := gzip.NewReader(bytes.NewReader(data))
        if err != nil {
            return nil, fmt.Errorf("buka gzip: %w", err)
        }
        defer reader.Close()

        var decompressed bytes.Buffer
        if _, err := io.Copy(&decompressed, reader); err != nil {
            return nil, fmt.Errorf("decompress: %w", err)
        }
        return decompressed.Bytes(), nil
    }

    return data, nil
}

Pola Penggunaan di Produksi #

Template Rendering ke Buffer #

import (
    "bytes"
    "html/template"
)

var tmplEmail = template.Must(template.New("email").Parse(`
Kepada: {{.Nama}}

Terima kasih telah mendaftar di layanan kami.
Kode verifikasi kamu: {{.Kode}}

Kode ini berlaku selama {{.DurasiMenit}} menit.
`))

type DataEmail struct {
    Nama         string
    Kode         string
    DurasiMenit  int
}

func renderEmail(data DataEmail) ([]byte, error) {
    var buf bytes.Buffer
    if err := tmplEmail.Execute(&buf, data); err != nil {
        return nil, fmt.Errorf("render email: %w", err)
    }
    return buf.Bytes(), nil
}

// Penggunaan
konten, err := renderEmail(DataEmail{
    Nama:        "Budi",
    Kode:        "123456",
    DurasiMenit: 10,
})
if err == nil {
    fmt.Println(string(konten))
}

Membangun CSV Manual #

func buatCSV(headers []string, rows [][]string) []byte {
    var buf bytes.Buffer

    // Tulis header
    for i, h := range headers {
        if i > 0 {
            buf.WriteByte(',')
        }
        buf.WriteString(escapeCSV(h))
    }
    buf.WriteByte('\n')

    // Tulis baris data
    for _, row := range rows {
        for i, cell := range row {
            if i > 0 {
                buf.WriteByte(',')
            }
            buf.WriteString(escapeCSV(cell))
        }
        buf.WriteByte('\n')
    }

    return buf.Bytes()
}

func escapeCSV(s string) string {
    // Kutip jika mengandung koma, newline, atau kutip
    if bytes.ContainsAny([]byte(s), ",\"\n\r") {
        return `"` + strings.ReplaceAll(s, `"`, `""`) + `"`
    }
    return s
}

// Penggunaan
csv := buatCSV(
    []string{"ID", "Nama", "Email", "Kota"},
    [][]string{
        {"1", "Budi Santoso", "[email protected]", "Jakarta"},
        {"2", "Ani", "[email protected]", "Bandung, Jawa Barat"},
        {"3", "Charlie", `char"[email protected]`, "Surabaya"},
    },
)
os.WriteFile("output.csv", csv, 0644)

Buffer Pool dengan sync.Pool #

import "sync"

// Pool bytes.Buffer untuk menghindari alokasi berulang
var bufPool = sync.Pool{
    New: func() any {
        return new(bytes.Buffer)
    },
}

func ambilBuffer() *bytes.Buffer {
    buf := bufPool.Get().(*bytes.Buffer)
    buf.Reset() // PENTING: reset sebelum digunakan
    return buf
}

func kembalikanBuffer(buf *bytes.Buffer) {
    // Jangan kembalikan buffer yang terlalu besar ke pool
    // untuk menghindari menahan memori berlebih
    if buf.Cap() <= 64*1024 { // 64 KB
        bufPool.Put(buf)
    }
}

func prosesRequest(data []byte) string {
    buf := ambilBuffer()
    defer kembalikanBuffer(buf)

    // Gunakan buf untuk membangun response
    buf.WriteString(`{"status":"ok","data":`)
    buf.Write(data)
    buf.WriteByte('}')

    return buf.String()
}

Streaming Data Besar tanpa Load ke Memori #

// Proses file besar baris per baris tanpa load semua ke memori
func prosesFileBesar(path string) error {
    f, err := os.Open(path)
    if err != nil {
        return fmt.Errorf("buka file: %w", err)
    }
    defer f.Close()

    var (
        buf      = make([]byte, 64*1024) // buffer 64 KB
        leftover []byte                  // sisa dari pembacaan sebelumnya
        baris    int
    )

    for {
        n, err := f.Read(buf)
        if n > 0 {
            // Gabungkan sisa sebelumnya dengan data baru
            chunk := append(leftover, buf[:n]...)
            leftover = nil

            // Proses setiap baris lengkap
            for {
                idx := bytes.IndexByte(chunk, '\n')
                if idx < 0 {
                    // Tidak ada newline — simpan sebagai sisa
                    leftover = chunk
                    break
                }
                baris++
                prosesBarisData(chunk[:idx])
                chunk = chunk[idx+1:]
            }
        }

        if err == io.EOF {
            // Proses sisa akhir jika tidak diakhiri newline
            if len(leftover) > 0 {
                baris++
                prosesBarisData(leftover)
            }
            break
        }
        if err != nil {
            return fmt.Errorf("baca file: %w", err)
        }
    }

    fmt.Printf("Diproses %d baris\n", baris)
    return nil
}

bytes vs strings — Kapan Mana #

flowchart TD
    Q{"Data yang dikerjakan?"} --> Str["string\n(immutable, sudah ada)"]
    Q --> ByteSlice["[]byte\n(mutable, dari jaringan/file)"]
    Q --> Both["Campuran keduanya"]

    Str --> UseStr["Gunakan package strings\n+ strings.Builder\ntanpa konversi"]

    ByteSlice --> UseBytes["Gunakan package bytes\n+ bytes.Buffer\ntanpa konversi"]

    Both --> Consider["Tentukan representasi utama\nminimalkan konversi\nbytes → string: string(b)\nstring → bytes: []byte(s)"]

    Consider --> Rule["Aturan: konversi menghasilkan salinan\nJika fungsi butuh []byte, hindari\nstring → []byte → string"]

    style UseStr fill:#e8f5e9
    style UseBytes fill:#e3f2fd
    style Rule fill:#fff3e0
// ANTI-PATTERN: konversi bolak-balik yang tidak perlu
func prosesDataBuruk(data []byte) []byte {
    s := string(data)           // salinan pertama: []byte → string
    s = strings.ToUpper(s)     // proses
    s = strings.TrimSpace(s)   // proses
    return []byte(s)           // salinan kedua: string → []byte
}

// BENAR: tetap di []byte, gunakan bytes package
func prosesDataBaik(data []byte) []byte {
    result := bytes.ToUpper(data)
    return bytes.TrimSpace(result)
}

// Kapan konversi MEMANG diperlukan:
// 1. Map/switch case dengan string literal
switch string(data[:4]) {
case "HTTP", "POST", "GET ":
    // proses
}

// 2. Saat fungsi eksternal hanya menerima string
log.Println(string(data)) // log.Println butuh string

// 3. Saat menyimpan ke struct yang bertipe string
pengguna.Nama = string(namaBytes)

Kapan Beralih ke Alternatif #

Tetap gunakan bytes jika:
  ✓ Manipulasi []byte: pencarian, trim, split, replace
  ✓ bytes.Buffer untuk membangun []byte secara inkremental
  ✓ bytes.Reader untuk meneruskan []byte sebagai io.Reader
  ✓ Pemrosesan data biner (protokol, format file)
  ✓ Bekerja dengan data dari jaringan atau file yang secara natural berupa []byte

Gunakan strings jika:
  ✗ Data sudah dalam bentuk string dan akan tetap string
  ✗ Manipulasi string: strings.Contains, strings.Split, dll

Gunakan strings.Builder jika:
  ✗ Membangun string tanpa butuh io.Reader/Writer
  ✗ Lebih efisien dari bytes.Buffer untuk pure string output

Gunakan bufio jika:
  ✗ Buffered I/O dari file atau koneksi jaringan
  ✗ Membaca per baris dari stream yang besar
  ✗ Parsing teks baris per baris dengan Scanner

Gunakan encoding/binary jika:
  ✗ Membaca/menulis integer dengan endianness tertentu dari []byte
  ✗ Parsing format biner yang terstruktur

Ringkasan #

  • bytes adalah cermin strings — hampir semua fungsi strings ada padanannya di bytes. Pilih berdasarkan tipe data: stringstrings, []bytebytes.
  • Hindari konversi []bytestring yang tidak perlu — setiap konversi menghasilkan salinan di memori. Jika data datang sebagai []byte, proses sebagai []byte sampai selesai.
  • bytes.Equal(a, b) lebih efisien dari string(a) == string(b) — tidak ada alokasi string sementara, bandingkan langsung byte per byte.
  • bytes.Buffer mengimplementasikan io.Reader dan io.Writer — berguna saat perlu buffer yang bisa dibaca setelah ditulis, atau saat fungsi eksternal membutuhkan io.Reader.
  • strings.Builder untuk pure string building — lebih efisien dari bytes.Buffer saat output akhir adalah string dan tidak perlu membaca kembali sebagai Reader.
  • bytes.Reader untuk mengubah []byte menjadi io.Reader — mendukung seeking (Seek), berguna untuk rewind atau pembacaan ulang dari posisi berbeda.
  • Gunakan sync.Pool untuk bytes.Buffer di hot path HTTP handler — hindari alokasi buffer baru setiap request dengan mendaur ulang buffer yang sudah ada.
  • bytes.Buffer.Reset() mengosongkan buffer tanpa melepas kapasitas yang sudah dialokasikan — reuse buffer dengan Reset jauh lebih efisien dari membuat buffer baru.
  • Untuk data besar, proses secara streaming dengan buffer kecil daripada load semua ke memori — gunakan io.Copy atau loop baca manual dengan []byte sebagai buffer.

← Sebelumnya: Sort   Berikutnya: Bufio →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact