Rust punya dua tipe teks utama. String adalah teks milik sendiri yang bisa ditambah dan diubah. &str (dibaca string slice) adalah pinjaman untuk membaca teks yang sudah ada di tempat lain. Bayangin kasir Kedai Martabak Bang Rusdi. Nota pesanan yang dia tulis adalah miliknya: bisa ditambah "1x es teh" di bawahnya, dan kalau diserahkan ke dapur, kasir gak memegangnya lagi. Itu String. Tulisan di papan menu cuma bisa ditunjuk dan dibaca: "yang ini, Martabak Manis". Menunjuk gak mengambil apa-apa dan gak bisa menambah tulisan. Itu &str.
🧪 Tebakan cepat: kasir menggabung nota dengan
+, lalu mencetak nota lamanya lagi:let nota = String::from("2x martabak manis"); let lengkap = nota + ", 1x es teh"; println!("{}", lengkap); println!("{}", nota);Lolos compile? Tebak dulu, jawabannya ada di bagian "Menambah dan menggabung teks".
String dan &str
fn cetak_menu(nama: &str) {
println!("Menu: {}", nama);
}
fn main() {
let es: &str = "Es teh";
let nota: String = String::from("Martabak telur");
let nota2 = "Martabak manis".to_string();
cetak_menu(es);
cetak_menu(¬a);
cetak_menu(¬a2);
}
Menu: Es teh
Menu: Martabak telur
Menu: Martabak manis
- Teks yang ditulis langsung di kode,
"Es teh", bertipe&str. Teksnya tersimpan di dalam program itu sendiri, dan kamu cuma meminjamnya. String::from("...")dan"...".to_string()membuatString: salinan milik sendiri yang bisa ditambah.¬ameminjam isiStringsebagai&str. Karena itu fungsi yang cuma membaca teks sebaiknya menerima&str: ia bisa dipanggil dengan teks literal maupun&String.- Kalau parameternya
String, memanggilnya dengan"Es teh"ditolak:expected `String`, found `&str`, dengan saran"Es teh".to_string().
Menambah dan menggabung teks
fn main() {
let mut nota = String::from("2x martabak manis");
nota.push_str(", 1x martabak telur");
nota.push('!');
println!("{}", nota);
let total = 102000;
let struk = format!("{} = Rp{}", nota, total);
println!("{}", struk);
println!("{}", nota);
}
2x martabak manis, 1x martabak telur!
2x martabak manis, 1x martabak telur! = Rp102000
2x martabak manis, 1x martabak telur!
.push_str(teks)menambah teks di ujung,.push(karakter)menambah satuchar(kutip tunggal). Keduanya mengubahnota, jadi wajiblet mut.format!bekerja sepertiprintln!, tapi hasilnya disimpan sebagaiStringbaru, bukan dicetak.notacuma dipinjam, jadi masih bisa dipakai.
+ juga menggabung, tapi caranya beda. Ini kode tebakan tadi:
error[E0382]: borrow of moved value: `nota`
--> src/main.rs:5:20
|
2 | let nota = String::from("2x martabak manis");
| ---- move occurs because `nota` has type `String`, which does not implement the `Copy` trait
3 | let lengkap = nota + ", 1x es teh";
| -------------------- `nota` moved due to usage in operator
4 | println!("{}", lengkap);
5 | println!("{}", nota);
| ^^^^ value borrowed here after move
|
note: calling this operator moves the left-hand side
+ mengambil String di kirinya, menempelkan teks di kanannya, lalu menyerahkan hasilnya ke lengkap. Setelah itu nota sudah kosong. Itu jawaban tebakannya: gak lolos. Sisi kanan + harus &str, jadi String di kanan ditulis dengan &: nota + &tambahan. Kalau semua bagiannya masih mau dipakai, pakai format!.
len() menghitung byte, bukan huruf
Teks Rust disimpan sebagai UTF-8: huruf biasa 1 byte, huruf seperti ê 2 byte. Kamu sudah melihat "Crêpe".len() 6 di pelajaran tipe data. Memotong teks dengan &menu[awal..akhir] juga memakai nomor byte:
fn main() {
let menu = String::from("Crêpe");
println!("len: {}", menu.len());
println!("chars: {}", menu.chars().count());
println!("{}", &menu[0..2]);
println!("{}", &menu[0..4]);
println!("{}", &menu[0..3]);
}
len: 6
chars: 5
Cr
Crê
thread 'main' (23389148) panicked at src/main.rs:7:25:
end byte index 3 is not a char boundary; it is inside 'ê' (bytes 2..4 of string)
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
| Byte | 0 | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|---|
| Isi | C |
r |
ê |
ê |
p |
e |
êmemakai byte 2 dan 3.&menu[0..4]berhenti tepat setelahê, jadiCrê.&menu[0..3]memotongêdi tengah. Teksnya jadi rusak, jadi Rust memilih panic saat jalan.- Karena itu
menu[0](satu huruf dengan nomor) gak ada di Rust: ditolak denganthe type `str` cannot be indexed by `{integer}`. Untuk huruf, pakai.chars():menu.chars().count()memberi 5, danmenu.chars().nth(2)memberiSome('ê').
Mengolah baris pesanan
Pembeli mengetik " 2x Martabak Manis ", lengkap dengan spasi berlebih. Kasir perlu jumlah porsi dan nama menunya:
fn main() {
let baris = " 2x Martabak Manis ";
let rapi = baris.trim();
println!("[{}]", rapi);
let bagian: Vec<&str> = rapi.split(' ').collect();
println!("{:?}", bagian);
let porsi: u32 = bagian[0].replace("x", "").parse().unwrap();
let menu = bagian[1..].join(" ").to_lowercase();
println!("{} porsi {}", porsi, menu);
println!("{}", menu.contains("manis"));
println!("{}", menu.to_uppercase());
println!("{}", porsi * 32000);
}
[2x Martabak Manis]
["2x", "Martabak", "Manis"]
2 porsi martabak manis
true
MARTABAK MANIS
64000
.trim()membuang spasi di awal dan akhir. Hasilnya&stryang meminjambaris..split(' ')memecah di tiap spasi. Hasilnya bukan daftar jadi, tapi potongan yang diambil satu per satu, jadi.collect()mengumpulkannya keVec<&str>. Kalau cuma perlu jumlahnya,.count():"hello, rust".split(", ").count()memberi 2..replace("x", "")membuat String baru tanpa hurufx, lalu.parse()mengubah"2"jadi angka.bagian[1..].join(" ")menyambung kata nomor 1 dan seterusnya dengan spasi..to_lowercase()dan.to_uppercase()membuat String baru,.contains("manis")memberitrueataufalse. Teks aslinya gak berubah.
⚠️ Jebakan yang sering ketemu
- Memakai String setelah
+.+memindahkan String di kirinya. Pakaiformat!kalau semuanya masih dipakai. - Mengira
len()itu jumlah huruf.len()menghitung byte UTF-8. Jumlah huruf:.chars().count(). - Memotong di tengah huruf.
&menu[0..3]pada"Crêpe"panic:is not a char boundary. teks[0]. String gak bisa dibaca dengan satu nomor. Pakai.chars().nth(0).- Parameter
Stringuntuk fungsi yang cuma membaca. Pakai&str, supaya bisa dipanggil dengan literal maupun&String.
TL;DR: String adalah teks milik sendiri yang bisa ditambah (String::from, .to_string(), push_str, push), &str adalah pinjaman untuk membaca (teks literal, ¬a, hasil trim). Fungsi yang cuma membaca menerima &str. format! membuat String baru tanpa mengambil apa-apa, sedangkan + memindahkan String di kirinya (borrow of moved value). len() menghitung byte UTF-8 ("Crêpe" 6), chars().count() menghitung huruf (5), dan memotong di tengah huruf membuat panic. trim, split (lalu collect atau count), replace, contains, to_uppercase, dan to_lowercase mengolah teks tanpa mengubah aslinya.