Slice

Tipe &[T] dan &str sebagai tampilan yang dipinjam atas data.

Daftar isi

Topik ini membahas slice, yaitu tampilan yang dipinjam atas bagian dari deret data tanpa memiliki data tersebut. Pembahasan mencakup irisan teks bertipe &str, irisan array dan vektor bertipe &[T], sintaks rentang yang dipakai untuk membuatnya, serta makna indeks byte pada teks UTF-8. Setelah membaca topik ini, fungsi dapat ditulis dengan parameter &str atau &[T] agar menerima String, literal teks, array, maupun Vec sekaligus, dan batas keamanan pengirisan teks dapat dihindari sejak merancang kode.

Irisan teks dengan &str#

Slice teks adalah referensi ke bagian dari sebuah String, dan tipenya ditulis &str. Penciptaannya memakai sintaks rentang di dalam kurung siku: &teks[awal..akhir] menghasilkan potongan mulai dari byte ke-awal sampai sebelum byte ke-akhir, sehingga batas awal bersifat inklusif dan batas akhir eksklusif. Kedua batas boleh dihilangkan: &teks[..n] berawal dari awal teks, &teks[n..] berakhir di akhir teks, dan &teks[..] mencakup seluruhnya. Karena sebuah slice hanya menyimpan penunjuk dan panjang, penyalinannya tidak pernah menggandakan isi teksnya.

Diagram berikut memetakan teks suhu ruangan ke dalam sel byte dan menandai wilayah yang dicakup &s[0..4]. Perhatikan bahwa indeks berada di antara sel, bukan pada karakter, dan bahwa empat byte pertama tepat membentuk kata suhu.

String "suhu ruangan" (satu byte per sel)suhuruangan0123456789101112&s[0..4] = "suhu"indeks batas awal inklusif, batas akhir eksklusif
Slice &s[0..4] sebagai jendela berukuran empat byte pada String

Fungsi pencari kata pertama berikut mengembalikan &str alih-alih posisi angka, sehingga hasilnya tetap berguna selama teks asalnya masih hidup. Tanda tangan &str pada parameternya membuat fungsi ini menerima &String maupun literal teks, karena referensi ke String dapat dipakai sebagai &str lewat koersi deref, yaitu pengubahan bentuk &String menjadi &str yang dilakukan kompilator secara otomatis pada titik pemakaian.

rust kata-pertama.rs
fn kata_pertama(teks: &str) -> &str {
    match teks.find(' ') {
        Some(posisi) => &teks[..posisi],
        None => teks,
    }
}

fn main() {
    let kalimat = String::from("suhu ruangan normal");
    let kata = kata_pertama(&kalimat); // &String berubah menjadi &str
    println!("Kata pertama: {}", kata);

    let literal = "tekanan udara naik";
    let kata2 = kata_pertama(literal);
    println!("Kata pertama: {}", kata2);
}
text Keluaran
Kata pertama: suhu
Kata pertama: tekanan

Keluaran tersebut memperlihatkan satu fungsi yang melayani dua sumber data sekaligus: kalimat bertipe String yang dimiliki program, dan literal yang merupakan irisan ke data teks di dalam berkas biner program. Metode find(' ') mengembalikan posisi byte spasi pertama, dan rentang ..posisi menghasilkan irisan sampai sebelum spasi tersebut. Apabila tidak ada spasi, seluruh teks dikembalikan apa adanya, sehingga fungsi ini tidak memiliki kasus khusus untuk teks satu kata.

Irisan array dan tipe &[T]#

Konsep slice berlaku umum untuk semua deret yang berdekatan di memori, bukan hanya teks. Array bertipe [T; N] dan Vec<T> dapat diiriskan menjadi &[T], dan tipe hasilnya menyimpan penunjuk ke elemen pertama beserta jumlah elemennya. Ukuran tipe &[T] tetap sepanjang dua bilangan meskipun jumlah elemen yang diiriskan berapa pun, karena jumlahnya dibawa sebagai data saat program berjalan.

rust irisan-array.rs
fn main() {
    let angka = [10, 20, 30, 40, 50];

    let tiga_pertama: &[i32] = &angka[..3];
    let dua_terakhir: &[i32] = &angka[3..];

    println!("Tiga pertama : {:?}", tiga_pertama);
    println!("Dua terakhir  : {:?}", dua_terakhir);
    println!("Total elemen  : {}", angka.len());
}
text Keluaran
Tiga pertama : [10, 20, 30]
Dua terakhir  : [40, 50]
Total elemen  : 5

Program tersebut mengambil tiga elemen pertama dan dua elemen terakhir dari array yang sama tanpa menyalin isinya. Kedua irisan berbagi data dengan array angka, sehingga aturan borrowing berlaku sebagaimana pada referensi biasa: selama sebuah irisan masih hidup, pemilik datanya tidak boleh memperoleh pinjaman tulis. Sintaks rentangnya juga sama dengan irisan teks, termasuk bentuk ..n dan n... Rentang yang melebihi panjang deret ditolak saat program berjalan: pengirisan &angka[0..8] pada array berisi lima elemen menghentikan program dengan panic yang menyebut indeks yang diminta beserta panjang riil deretnya. Gunakan angka.len() sebagai sumber batas atas agar rentang selalu berada di dalam deret.

Slice adalah pinjaman, bukan salinan#

Slice tidak mengalokasikan memori dan tidak memiliki data yang dilihatnya, melainkan meminjam data tersebut dari pemiliknya. Konsekuensinya mengikuti aturan borrowing: banyak irisan baca dapat hidup bersamaan atas satu data, tetapi irisan yang masih dipakai akan menghalangi pinjaman tulis atas data asalnya. Pola ini membuat operasi seperti pencarian kata pada teks besar dapat berjalan tanpa menggandakan isi teksnya sedikit pun. Irisan juga dapat dibuat dari irisan lain tanpa batas kedalaman, sehingga pencarian bertingkat cukup mempersempit jendela yang sudah ada. Setiap penyempitan hanya mengganti penunjuk beserta panjangnya, dan tidak pernah menyentuh data di baliknya.

Literal teks juga adalah slice. Literal "tekanan udara naik" pada berkas kata-pertama.rs bertipe &str yang menunjuk data teks yang tertanam di dalam berkas biner program, sehingga literal tersebut tidak memerlukan alokasi heap saat dipakai. Karena data itu hidup selama program berjalan, literal teks dapat dipakai di mana saja tanpa perhitungan umur. Sifat khusus tersebut dibahas pada topik Lifetime Putaran Pertama.

Indeks byte dan batas karakter#

Teks Rust disimpan sebagai byte UTF-8, sehingga indeks rentang pada slice teks adalah nomor byte, bukan nomor karakter. Untuk teks yang seluruh karakternya berukuran satu byte, misalnya "suhu ruangan", kedua makna tersebut berimpit dan tidak timbul kebingungan. Untuk teks dengan karakter multibyte, keduanya berbeda: huruf é pada "café" menempati dua byte, sehingga teks tersebut berukuran lima byte meskipun jumlah karakternya empat, dan nomor byte tidak lagi sejajar dengan nomor karakter.

Pesan panic tersebut tidak muncul pada teks contoh di topik ini karena seluruh batas rentangnya sengaja dipilih pada batas kata dan batas karakter ASCII. Untuk pengolahan teks umum, hitung batas dengan metode yang sadar karakter, misalnya find, char_indices, atau chars().position(), lalu gunakan hasilnya sebagai indeks rentang.

Kesalahan umum#

Kesalahan pertama adalah menganggap indeks slice teks sebagai nomor karakter. Nomor tersebut adalah byte, sehingga panjang teks beraksen atau berhuruf non-Latin tidak sama dengan jumlah karakternya, dan rentang yang dihitung per karakter dapat memotong batas karakter. Fungsi len() pada str pun menghitung byte, bukan karakter, sehingga teks.len() dan teks.chars().count() dapat menghasilkan dua angka berbeda untuk teks yang sama. Perbaikannya adalah menghitung posisi lewat metode yang bekerja pada karakter, atau memproses teks lewat chars() alih-alih rentang byte.

Kesalahan kedua adalah menulis parameter fungsi sebagai &String atau &Vec<T> padahal isi fungsinya cukup memakai irisan. Tanda tangan &str dan &[T] menerima lebih banyak jenis pemanggil, termasuk literal dan array, tanpa mengorbankan kemampuan apa pun. Aturan praktisnya sederhana: terima irisan pada parameter, dan simpan kepemilikan hanya pada tempat data itu benar-benar disimpan. Pengecualian hanya berlaku bila fungsi memang harus menumbuhkan atau menyimpan datanya, misalnya membangun String baru dari potongan-potongan masukan.

Ringkasan#

  • Slice adalah tampilan yang dipinjam atas deret data: &str untuk teks dan &[T] untuk elemen bertipe lain.
  • Rentang &teks[awal..akhir] memakai indeks byte dengan batas awal inklusif dan batas akhir eksklusif, serta memungkinkan bentuk ..n, n.., dan ...
  • Parameter &str menerima String, literal, maupun irisan lain melalui koersi deref, sehingga fungsinya menjadi lebih serbaguna.
  • Indeks yang tidak jatuh di batas karakter menyebabkan panic saat berjalan, sehingga batas rentang pada teks UTF-8 dihitung dengan metode yang sadar karakter.

Referensi#