Lewati ke konten

Konversi tipe di Rust

Rust tidak akan mengubah satu tipe ke tipe lain secara otomatis. Kalau kamu mencampur i32 dan f64 dalam satu ekspresi, kodenya tidak akan bisa dikompilasi. Kamu harus mengonversinya secara eksplisit. Ini memang disengaja: konversi diam-diam adalah sumber bug yang umum di bahasa lain.

Pakai keyword as untuk melakukan casting (mengubah) satu tipe angka ke tipe angka lain:

fn main() {
let x: i32 = 10;
let y: f64 = x as f64; // i32 -> f64
println!("{y}");
}
▶ Coba sendiri

Ini lebih penting daripada kelihatannya. Coba bagi dua bilangan bulat tanpa casting, dan kamu akan mendapat pembagian bilangan bulat, bukan hasil desimal yang mungkin kamu harapkan:

fn main() {
let a = 10;
let b = 3;
println!("{}", a / b); // 3 (pembagian bilangan bulat)
println!("{}", a as f64 / b as f64); // 3.3333333333333335
}
▶ Coba sendiri

Casting ke tipe lebih kecil bisa kehilangan data

Section titled “Casting ke tipe lebih kecil bisa kehilangan data”

Casting dari tipe yang lebih besar ke yang lebih kecil (misalnya i32 ke u8) tidak akan gagal. Nilainya hanya dipotong, dan hasilnya bisa mengejutkan:

fn main() {
let big: i32 = 300;
let small = big as u8; // u8 hanya bisa menampung 0-255
println!("{small}"); // 44, bukan 300: nilainya berputar (wrap around)
}
▶ Coba sendiri

as bisa dipakai untuk beberapa tipe lain selain bilangan bulat. Masing-masing punya aturan yang perlu kamu tahu:

fn main() {
println!("{}", 3.99_f64 as i32); // 3: desimalnya dibuang
println!("{}", -3.99_f64 as i32); // -3: dipotong ke arah nol, bukan ke bawah
println!("{}", 3.99_f64.round() as i32); // 4: bulatkan dulu kalau itu yang kamu mau
println!("{}", 300.0_f64 as u8); // 255: terlalu besar, jadi berhenti di nilai maksimum
println!("{}", -5.0_f64 as u8); // 0: terlalu kecil, jadi berhenti di nilai minimum
println!("{}", 'A' as u32); // 65: nomor kode sebuah char
println!("{}", 97_u8 as char); // a: dan kembali lagi
println!("{}", true as i32); // 1 (false akan menjadi 0)
}

Output:

3
-3
4
255
0
65
a
1
▶ Coba sendiri

Beberapa hal yang bisa kamu ambil dari sini:

  • Float ke bilangan bulat membuang semua angka di belakang koma. Nilainya tidak dibulatkan. Panggil .round() dulu kalau kamu ingin bilangan bulat terdekat.
  • Float di luar rentang ditahan (clamp) ke nilai terdekat yang masih muat (300.0 menjadi 255 di u8). Ini berbeda dengan bilangan bulat, yang nilainya berputar (300 menjadi 44 di contoh sebelumnya).
  • Karakter punya angka di baliknya (code point Unicode-nya). 'A' as u32 menghasilkan 65. Hanya u8 yang bisa di-cast langsung kembali menjadi char. Untuk angka yang lebih besar, kamu memakai char::from_u32, karena tidak semua angka adalah karakter yang valid.
  • Boolean berubah menjadi 1 atau 0. Arah sebaliknya tidak bisa: 1 as bool tidak akan bisa dikompilasi. Pakai perbandingan seperti n != 0 sebagai gantinya.

Casting dengan as hanya bisa dipakai antartipe angka. Untuk mengubah teks menjadi angka, misalnya sesuatu yang diketik pengguna, pakai .parse():

fn main() {
let text = "42";
let number: i32 = text.parse().expect("bukan angka yang valid");
println!("{}", number + 8);
}

Output:

50
▶ Coba sendiri

.parse() bisa gagal (bagaimana kalau teksnya sama sekali bukan angka?), jadi yang dikembalikan adalah sebuah Result, bukan langsung angkanya. Kita belum membahas Result. Untuk sekarang, .expect("pesan") adalah cara sederhana untuk bilang “crash dengan pesan ini kalau gagal.” Cara yang benar untuk menanganinya akan kamu lihat di halaman Result.

Arah sebaliknya lebih sederhana. Hampir semua tipe bisa diubah menjadi String dengan .to_string():

fn main() {
let number = 42;
let text = number.to_string();
println!("{}", text + "!");
}
▶ Coba sendiri

Simpan tiga nilai ujian sebagai i32: 7, 8, dan 10. Tampilkan jumlahnya, lalu rata-ratanya dengan dua angka di belakang koma. Petunjuk: kalau kamu membagi dua bilangan bulat, hasilnya 8, bukan 8.33, jadi cast jumlahnya ke f64 lebih dulu. {:.2} menampilkan dua angka desimal.

Lihat solusi
fn main() {
let a: i32 = 7;
let b: i32 = 8;
let c: i32 = 10;
let sum = a + b + c;
let average = sum as f64 / 3.0; // 25.0 / 3.0, bukan 25 / 3
println!("jumlah: {sum}");
println!("rata-rata: {:.2}", average);
}

Output:

jumlah: 25
rata-rata: 8.33
▶ Coba sendiri

Sebuah toko memberimu harga dalam bentuk teks, "4.50", dan jumlah barang sebagai i32, yaitu 3. Ubah teksnya menjadi f64, kalikan dengan jumlah barang, lalu tampilkan Total: 13.5. Petunjuk: kamu butuh kedua alat dari halaman ini: .parse() untuk teksnya dan as f64 untuk jumlah barangnya.

Lihat solusi
fn main() {
let price_text = "4.50";
let quantity: i32 = 3;
let price: f64 = price_text.parse().expect("bukan harga yang valid");
let total = price * quantity as f64; // f64 * f64
println!("Total: {total}");
}

Output:

Total: 13.5
▶ Coba sendiri

Tanpa as f64, Rust akan menolak mengalikan f64 dengan i32, karena Rust tidak pernah mencampur tipe angka untukmu.

  • Rust tidak pernah mengonversi tipe secara otomatis. Kamu selalu melakukan casting secara eksplisit.
  • Pakai as untuk mengonversi antartipe angka: x as f64, x as u8.
  • Casting ke tipe yang lebih kecil memotong nilai secara diam-diam. Tidak ada error, jadi hati-hati.
  • Float ke bilangan bulat membuang desimalnya (pakai .round() dulu untuk membulatkan). char dan bool juga bisa di-cast ke angka.
  • Pakai .parse() untuk mengubah teks menjadi angka, dan .to_string() untuk arah sebaliknya.

Kuis singkat

1. Apa yang terjadi kalau kamu membagi dua bilangan bulat seperti 10 / 3 di Rust tanpa casting?

2. Apa yang terjadi kalau kamu melakukan casting 300_i32 as u8?

3. Apa yang dikembalikan .parse() saat mengubah string menjadi angka, mengingat konversinya bisa gagal?

Skor: 0 / 3