Mengambil potongan teks dan merangkai potongan-potongan itu kembali adalah dua pekerjaan teks yang paling sering muncul: memisahkan nama depan dari nama belakang, memecah baris data CSV, atau menggabungkan daftar hari menjadi satu kalimat. Kompilator sudah mengisyaratkan alatnya di Chapter sebelumnya — “string indices are ranges of usize” — dan sekarang kita buka kotaknya. Chapter ini memuat tiga kelompok perkakas: slice rentang byte untuk memotong, split untuk memecah menurut pemisah, serta join, concat, dan repeat untuk menyusun kembali. Ketiganya bekerja di atas representasi UTF-8, sehingga satu aturan keselamatan akan menyertainya: batas potongan harus jatuh di antara character.
Memotong dengan Slice Rentang Byte #
Chapter Slice dan Referensi memperkenalkan slice sebagai pinjaman sepotong data. Untuk teks, sintaksnya adalah &teks[mulai..akhir] dengan satuan indeks byte — bukan character. Slice meminjam: tidak ada penyalinan teks, hanya penunjuk ke posisi awal plus panjang potongan. Bentuk &teks[..akhir] dan &teks[mulai..] juga sah untuk potongan dari awal atau sampai akhir:
fn main() {
let sentence = "Belajar Rust itu menyenangkan";
let subject = &sentence[0..7];
let verb = &sentence[8..12];
let rest = &sentence[13..];
println!("subject: {}", subject);
println!("verb: {}", verb);
println!("rest: {}", rest);
}Output yang Dihasilkan:
subject: Belajar
verb: Rust
rest: itu menyenangkanMari kita bedah kode di atas baris per baris:
- Baris 2 (
let sentence = "Belajar Rust itu menyenangkan";): teks acuan ASCII murni dengan 29 byte; satu byte satu character, sehingga menghitung indeksnya mudah. - Baris 3 (
let subject = &sentence[0..7];): pinjaman atas byte 0 sampai sebelum 7 — kataBelajar. Indeks akhir bersifat eksklusif: byte ke-7 (spasi) tidak ikut. - Baris 4 (
let verb = &sentence[8..12];): potongan byte 8 sampai 11 — kataRust. Karena polanya konsisten, indeks ini bisa dihitung dari posisi tiap kata. - Baris 5 (
let rest = &sentence[13..];): rentang terbuka di kanan mengambil dari byte 13 sampai akhir teks —itu menyenangkan. - Baris 6-8 (
println!…): ketiga slice dicetak. Semua slice menunjuk ke memori teks yang sama; tidak adaStringbaru yang dibuat.
Batas Character dan Bahaya Potongan di Tengah Huruf #
Aturan keselamatannya tegas: kedua ujung rentang harus jatuh di char boundary — posisi byte tempat sebuah character bermula atau berakhir. Teks ASCII selalu aman karena semua byte adalah batas. Teks non-ASCII tidak: huruf Rusia П menempati byte 0..2, sehingga memotong di byte 1 berarti mencacah huruf itu di tengah. Rust menolak menebak dan memilih berhenti:
fn main() {
let greeting = "Привет";
let first_letter = &greeting[0..1];
println!("huruf pertama: {}", first_letter);
}Output yang Dihasilkan:
thread 'main' (16387) panicked at main.rs:3:33:
end byte index 1 is not a char boundary; it is inside 'П' (bytes 0..2 of string)
note: run with `RUST_BACKTRACE=1` environment variable to display a backtraceBedah kejadiannya:
- Baris 2 (
let greeting = "Привет";): teks berisi 6 huruf Rusia yang masing-masing 2 byte — byte 0..2 milik hurufП. - Baris 3 (
let first_letter = &greeting[0..1];): rentang meminta byte 0 saja, padahal huruf pertama belum selesai di byte 1. Program berhenti dengan panic pada saat baris ini dieksekusi; baris 4 tidak pernah tercapai. - Pesan panic: teks pesannya sangat deskriptif — indeks akhir 1 bukan batas character, berada di dalam huruf
Пyang menempati byte 0 sampai 2. Angka(16387)adalah penanda thread internal yang nilainya berubah setiap eksekusi, jadi angka di mesin kita akan berbeda. Program keluar dengan kode 101, penanda panic.
Pintu amannya adalah get(rentang): metode ini memeriksa keabsahan rentang dan mengembalikan nilai bertipe Option — Some berisi potongan bila rentang sah, None bila tidak. Pola match dari Section Kontrol Alur menangani kedua kemungkinannya:
fn main() {
let file_name = "laporan_q1.pdf";
match file_name.get(0..7) {
Some(chunk) => println!("awalan: {}", chunk),
None => println!("rentang byte tidak valid"),
}
match file_name.get(0..100) {
Some(chunk) => println!("awalan: {}", chunk),
None => println!("rentang byte tidak valid"),
}
}Output yang Dihasilkan:
awalan: laporan
rentang byte tidak validBedah singkatnya:
- Baris 3 (
match file_name.get(0..7) {): rentang 0..7 sah pada teks 14 byte dan jatuh di antara kata, sehingga cabangSome(chunk)berjalan dan mencetaklaporan. - Baris 7 (
match file_name.get(0..100) {): rentang melebihi panjang teks sehingga tidak sah —getmemilih jalanNonealih-alih membuat program panic. TipeOptionini akan dibahas tuntas di Section Struktur Data; untuk sekarang,matchdengan dua cabangSome/Nonesudah cukup dipakai sebagai pola.
Memecah Teks dengan split dan split_whitespace #
Untuk memotong menurut pemisah, split(pemisah) memecah teks setiap kali bertemu separator lalu menyodorkan potongan-potongannya satu per satu — dan pola for menyambutnya dengan natural. Varian split_whitespace() memecah menurut spasi berturut-turut dan tab sekaligus, sambil membuang potongan kosong:
fn main() {
let csv = "Budi,Jakarta,1990";
for item in csv.split(',') {
println!("- {}", item);
}
let sentence = "Belajar Rust itu menyenangkan";
for word in sentence.split_whitespace() {
println!("kata: {}", word);
}
}Output yang Dihasilkan:
- Budi
- Jakarta
- 1990
kata: Belajar
kata: Rust
kata: itu
kata: menyenangkanMari kita telaah baris-barisnya:
- Baris 2 (
let csv = "Budi,Jakarta,1990";): satu baris data dengan koma sebagai pemisah. - Baris 3 (
for item in csv.split(',') {):split(',')memecah teks setiap koma; perulangan menerima tiga potongan:Budi,Jakarta, dan1990. - Baris 7 (
let sentence = "Belajar Rust itu menyenangkan";): kalimat dengan spasi tak beraturan — tiga spasi di satu tempat, dua di tempat lain. - Baris 8 (
for word in sentence.split_whitespace() {):split_whitespace()memperlakukan segala rentasan spasi sebagai satu pemisah, sehingga keluarannya bersih: empat kata tanpa potongan kosong.split(' ')polos akan menghasilkan potongan kosong di antara spasi berturut-turut.
Menggabungkan dengan join, concat, dan repeat #
Arah sebaliknya — merakit potongan menjadi teks utuh — dilayani tiga metode. join(pemisah) menjahit elemen-elemen teks dengan pemisah di sela-selanya; concat() menjahit tanpa pemisah; repeat(n) menggandakan teks sebanyak n kali. Ketiganya mengembalikan String milik sendiri — hasil penggabungan dialokasikan di heap:
fn main() {
let days = ["Senin", "Rabu", "Jumat"];
let joined = days.join(", ");
println!("join: {}", joined);
let parts = ["rust", "dan", "teks"];
println!("concat: {}", parts.concat());
let line = "-".repeat(20);
println!("{}| batas |{}", line, line);
}Output yang Dihasilkan:
join: Senin, Rabu, Jumat
concat: rustdanteks
--------------------| batas |--------------------Bedah kode di atas:
- Baris 2 (
let days = ["Senin", "Rabu", "Jumat"];): array tiga teks — struktur data yang sudah kita kenal di Section Dasar Sintaks. - Baris 3 (
let joined = days.join(", ");):joinmenyisipkan", "di antara tiap elemen dan mengembalikanStringbaruSenin, Rabu, Jumat— pemisah tidak tertempel di awal maupun akhir. - Baris 5 (
println!("concat: {}", parts.concat());):concatmenempel elemen apa adanya tanpa pemisah, menghasilkanrustdanteks— untuk teks yang dibaca manusia,joinhampir selalu pilihan yang tepat. - Baris 6 (
let line = "-".repeat(20);):repeatmenggandakan"-"dua puluh kali menjadiString; hasilnya dipakai dua kali untuk menggambar garis batas.
Ringkasan #
Slice rentang byte memotong teks sebagai pinjaman tanpa penyalinan, split dan split_whitespace memecahnya menurut pemisah, sementara join, concat, dan repeat merakit potongan menjadi String baru. Aturan keselamatan menaungi semuanya: ujung rentang harus jatuh di char boundary, dan pelanggarannya dihukum panic saat program berjalan — lengkap dengan pesan yang menunjuk huruf mana yang tercacah. Untuk teks yang tidak terjamin batasnya, get dengan pola match mengubah kemungkinan panic menjadi cabang None yang bisa ditangani. Perkakas terakhir yang belum tersentuh adalah merangkai nilai non-teks — angka, tipe lain — menjadi kalimat, dan itu wilayah format! di Chapter berikutnya.