Setiap program yang mengolah teks melakukan tiga hal berulang: membaca sebagian string, menggabungkan potongan, dan mengubah tampilannya. Di banyak bahasa pemrograman, ketiga hal itu dilakukan dengan mengganti karakter langsung di tempat. Go memilih pendekatan berbeda: string bersifat immutable — isinya tidak dapat diubah setelah string dibuat. Pada chapter ini kita akan membuktikan aturan tersebut lewat percobaan baca-tulis, membaca pesan error compiler, lalu mempelajari cara idiomatik membangun string baru.

Membaca Byte di dalam String #

String di Go adalah rangkaian byte yang diindeks mulai dari nol. Operator indeks s[0] mengambil satu byte pada posisi pertama, dan hasilnya bertipe byte (alias dari uint8), bukan string. Membaca byte dengan cara ini sepenuhnya legal karena operasi baca tidak mengubah isi string:

go
s := "Halo"
fmt.Println(s[0], string(s[0]))

Output yang Dihasilkan:

text
72 H

Angka 72 adalah kode numerik huruf H menurut tabel ASCII, sedangkan konversi string(s[0]) mengubah byte tersebut kembali menjadi string satu karakter. Perlu dicatat bahwa indeks string selalu menghitung byte, bukan karakter — fakta ini penting saat teks memuat karakter multibyte, dan kita bedah tuntas pada chapter UTF-8 dan Unicode .

Penugasan Byte Ditolak Kompilator #

Sekarang kita coba kebalikannya: mengganti huruf pertama sebuah string dengan huruf lain. Berkas error-immutable.go pada folder chapter ini sengaja berisi penugasan s[0] = 'M':

go
package main

import "fmt"

func main() {
	s := "Halo, Go!"
	s[0] = 'M'
	fmt.Println(s)
}

Jalankan berkas tersebut untuk melihat penolakan compiler:

bash
go run error-immutable.go

Output yang Dihasilkan:

text
# command-line-arguments
./error-immutable.go:7:2: cannot assign to s[0] (neither addressable nor a map index expression)

Program tidak pernah berjalan: kompilasi berhenti dengan exit code 1. Baris pertama output, # command-line-arguments, adalah nama package sementara yang dibentuk go run saat kita menjalankan satu berkas. Baris kedua adalah pesan errornya: hasil indeks string bukan lokasi memori yang dapat dituju penugasan, sehingga byte string tidak bisa ditulis. Redakan pesan bisa berbeda antar versi Go, tetapi maknanya tetap: hanya elemen slice, array, dan map yang boleh menjadi sasaran assignment lewat indeks.

Membangun String Baru dengan Slicing dan Penggabungan #

Karena isi string tidak bisa diubah, mengganti teks berarti membuat string baru. Cara paling sederhana adalah mengiris string lama dengan operator slicing, lalu merangkai potongannya dengan operator +:

go
s := "Halo, Go!"
baru := s[:4] + ", Dunia " + s[6:]
fmt.Println(baru)

Output yang Dihasilkan:

text
Halo, Dunia Go!

Ekspresi s[:4] membentuk string Halo dari empat byte pertama, s[6:] mengambil sisa mulai byte keenam (Go!), dan operator + merangkai semuanya menjadi string baru. Variabel s tetap memuat Halo, Go! karena penggabungan tidak pernah menyentuh operand aslinya. Pola yang sama berlaku pada operator +=: setiap penugasan mengalokasikan string baru dan menyalin seluruh isi lama plus tambahannya, sehingga tiga kali pesan += "..." menghasilkan tiga salinan berukuran bertambah.

Konversi Bolak-Balik ke []byte #

Jika kita benar-benar perlu mengubah karakter di tempat, Go menyediakan pintu keluar yang jujur: konversi ke []byte. Konversi ini menyalin isi string ke slice byte baru yang isinya boleh dimutasi:

go
b := []byte("Halo")
b[0] = 'M'
fmt.Println(string(b))

Output yang Dihasilkan:

text
Malo

Slice b berdiri sendiri terpisah dari string asalnya, sehingga mengganti b[0] hanya mengubah salinan. Konversi string(b) menyalin byte kembali menjadi string baru — alurnya bolak-balik, dua penyalinan. Perpindahan string ↔ []byte ini menjadi kunci saat bekerja dengan data byte hasil pembacaan berkas, sebagaimana dibahas pada chapter Package bytes .

Program Utuh #

Berkas immutable-demo.go menggabungkan seluruh percobaan di atas dalam satu program yang dapat dijalankan dari direktori chapter:

go
package main

import "fmt"

func main() {
	s := "Halo, Go!"

	// Membaca byte pada indeks 0: diperbolehkan.
	fmt.Println("kode byte :", s[0])
	fmt.Println("huruf     :", string(s[0]))

	// String baru dibangun dari potongan string lama.
	depan := s[:4]
	belakang := s[6:]
	baru := depan + ", Dunia " + belakang
	fmt.Println("baru      :", baru)

	// Operator += membuat alokasi string baru setiap kali.
	pesan := "Stok"
	pesan += " gudang"
	pesan += " penuh"
	fmt.Println("pesan     :", pesan)

	// Konversi bolak-balik []byte: isi slice boleh diubah.
	b := []byte(s)
	b[0] = 'M'
	fmt.Println("diubah    :", string(b))
	fmt.Println("asli      :", s)
}
bash
go run immutable-demo.go

Output yang Dihasilkan:

text
kode byte : 72
huruf     : H
baru      : Halo, Dunia Go!
pesan     : Stok gudang penuh
diubah    : Malo, Go!
asli      : Halo, Go!

Mari kita telaah baris demi baris program di atas:

  1. Baris 6 (s := "Halo, Go!"): mendeklarasikan string sembilan byte yang menjadi bahan percobaan; sejak baris ini isi s terkunci.
  2. Baris 9-10 (fmt.Println("kode byte ...")): membaca s[0] sebagai byte (72) dan sebagai string (H) — operasi baca indeks berjalan normal.
  3. Baris 13-16 (depan := s[:4] dst.): merangkai string baru Halo, Dunia Go! dari dua irisan dan satu penggabungan; operand lama tetap utuh.
  4. Baris 19-22 (pesan += ...): memperluas string stok tiga kali; setiap += membuat salinan baru hingga terbentuk Stok gudang penuh.
  5. Baris 25-28 (b := []byte(s) dst.): menyalin string ke slice byte lalu mengubah byte pertama menjadi M; baris terakhir menegaskan s asli masih Halo, Go!.

Immutability membuat perilaku string mudah diprediksi: string yang sama dipakai di dua tempat tidak akan saling merusak. Imbasnya, pertanyaan “berapa panjang string” tidak lagi sesederhana hitungan byte ketika teks memuat aksen atau huruf kanji — pertanyaan itulah yang kita jawab pada chapter UTF-8 dan Unicode .

Referensi #

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

error-immutable.go

Unduh berkas
go
package main

import "fmt"

func main() {
	s := "Halo, Go!"
	s[0] = 'M'
	fmt.Println(s)
}

immutable-demo.go

Unduh berkas
go
package main

import "fmt"

func main() {
	s := "Halo, Go!"

	// Membaca byte pada indeks 0: diperbolehkan.
	fmt.Println("kode byte :", s[0])
	fmt.Println("huruf     :", string(s[0]))

	// String baru dibangun dari potongan string lama.
	depan := s[:4]
	belakang := s[6:]
	baru := depan + ", Dunia " + belakang
	fmt.Println("baru      :", baru)

	// Operator += membuat alokasi string baru setiap kali.
	pesan := "Stok"
	pesan += " gudang"
	pesan += " penuh"
	fmt.Println("pesan     :", pesan)

	// Konversi bolak-balik []byte: isi slice boleh diubah.
	b := []byte(s)
	b[0] = 'M'
	fmt.Println("diubah    :", string(b))
	fmt.Println("asli      :", s)
}