Setiap nilai di dunia nyata memiliki bentuk yang berbeda: jumlah penumpang berupa bilangan bulat, berat badan mengandung angka desimal, status pesanan hanya punya dua kemungkinan, dan nama pelanggan berupa rangkaian huruf. PHP menangani perbedaan ini secara longgar karena interpreter mengingat tipe setiap nilai secara dinamis. Rust memilih jalur sebaliknya: kompilator harus tahu tipe dan ukuran memori setiap nilai sebelum program dijalankan, sehingga mesin tidak perlu membawa informasi tipe pada saat runtime. Pada Chapter ini kita akan mengenal empat tipe skalair milik Rust, yaitu integer, floating point, boolean, dan char, beserta rentang nilainya.

Empat Kategori Tipe Skalair #

Tipe skalair adalah tipe yang menyimpan tepat satu nilai, lawan dari tipe komposit seperti tuple dan array yang mengelompokkan banyak nilai. Rust membagi tipe skalair menjadi empat kategori, dan setiap kategori menempati ukuran memori yang tetap:

  • Integer: bilangan bulat, misalnya i32 dan u8.
  • Floating point: bilangan desimal, yaitu f32 dan f64.
  • Boolean: nilai kebenaran true atau false, ditulis bool.
  • char: satu karakter Unicode, selalu berukuran 4 byte.

Keempat kategori tersebut akan kita bahas satu per satu dengan contoh kode nyata.

Tipe Integer #

Tipe integer dibedakan oleh dua hal: apakah nilainya boleh negatif (signed, diawali i) atau tidak (unsigned, diawali u), serta berapa byte memorinya. Tabel berikut merangkum pilihan yang tersedia:

TipeRentang nilaiUkuran
i8-128 s.d. 1271 byte
u80 s.d. 2551 byte
i16-32.768 s.d. 32.7672 byte
u160 s.d. 65.5352 byte
i32-2.147.483.648 s.d. 2.147.483.6474 byte
u320 s.d. 4.294.967.2954 byte
i64-9.223.372.036.854.775.808 s.d. 9.223.372.036.854.775.8078 byte
u640 s.d. 18.446.744.073.709.551.6158 byte
i128 / u128rentang sangat besar16 byte
isize / usizemengikuti arsitektur komputer4 atau 8 byte

Saat kita menulis bilangan bulat tanpa anotasi tipe, kompilator memilih i32 sebagai bawaan karena tipe tersebut dianggap cukup besar untuk kebutuhan umum sekaligus tetap cepat diproses. Kode berikut memakai beberapa tipe integer secara eksplisit:

rust
fn main() {
    let suhu_selisih: i8 = -40;
    let jumlah_roti: u8 = 200;
    let populasi_kota: u32 = 8_500_000;
    let populasi_negara: i64 = 275_000_000;

    println!("Selisih suhu: {} derajat", suhu_selisih);
    println!("Jumlah roti: {} buah", jumlah_roti);
    println!("Populasi kota: {} jiwa", populasi_kota);
    println!("Populasi negara: {} jiwa", populasi_negara);
}

Output yang Dihasilkan:

text
Selisih suhu: -40 derajat
Jumlah roti: 200 buah
Populasi kota: 8500000 jiwa
Populasi negara: 275000000 jiwa

Mari kita bedah pemilihan tipe pada tiap barisnya:

  1. Baris 2 (let suhu_selisih: i8 = -40;): nilai bisa negatif, sehingga tipe signed i8 tepat; angka -40 berada jauh di dalam rentang -128 s.d. 127.
  2. Baris 3 (let jumlah_roti: u8 = 200;): jumlah roti tidak mungkin negatif, sehingga u8 yang menyimpan 0 s.d. 255 sudah memadai.
  3. Baris 4 (let populasi_kota: u32 = 8_500_000;): tanda garis bawah pada 8_500_000 murni pemisah ribuan agar mudah dibaca; nilainya tetap 8500000.
  4. Baris 5 (let populasi_negara: i64 = 275_000_000;): angka besar untuk populasi negara disimpan pada i64 agar tersedia ruang tumbuh yang lebar.

Anotasi tipe menuntut kita jujur terhadap rentang. Kompilator langsung menolak nilai yang tidak muat:

rust
fn main() {
    let jumlah_roti: u8 = 300;
    println!("Jumlah roti: {}", jumlah_roti);
}

Pesan galatnya berbunyi:

text
error: literal out of range for `u8`
 --> main.rs:2:27
  |
2 |     let jumlah_roti: u8 = 300;
  |                           ^^^
  |
  = note: the literal `300` does not fit into the type `u8` whose range is `0..=255`
  = note: `#[deny(overflowing_literals)]` on by default

error: aborting due to 1 previous error

Galat tersebut menegaskan bahwa 300 tidak muat dalam u8 yang hanya menampung 0..=255. Kita harus memilih tipe yang lebih besar seperti u16, atau memperbaiki nilai datanya.

Bentuk Penulisan Literal Integer #

Rust menerima bilangan bulat dalam empat sistem bilangan sekaligus: desimal, heksadesimal, oktal, dan biner. Bentuk 0x, 0o, dan 0b masing-masing menjadi penanda sistem bilangannya, sedangkan awalan b'...' (hanya untuk satu karakter ASCII) menghasilkan nilai byte bertipe u8:

rust
fn main() {
    let desimal = 1_000_000;
    let heksadesimal = 0xFF;
    let oktal = 0o77;
    let biner = 0b1010_1010;
    let byte_a = b'A';

    println!("Desimal: {}", desimal);
    println!("Heksadesimal: {}", heksadesimal);
    println!("Oktal: {}", oktal);
    println!("Biner: {}", biner);
    println!("Byte: {}", byte_a);
}

Output yang Dihasilkan:

text
Desimal: 1000000
Heksadesimal: 255
Oktal: 63
Biner: 170
Byte: 65

Nilai 0xFF adalah 255 dalam heksadesimal, 0o77 adalah 63 dalam oktal, 0b1010_1010 adalah 170 dalam biner, dan b'A' adalah 65 karena huruf A memiliki kode ASCII 65. Keempat literal tersebut menyimpan bilangan bulat biasa; sistem bilangan hanya memengaruhi cara kita menulisnya, bukan tipe hasilnya.

Tipe Floating Point #

Bilangan desimal ditangani dua tipe: f64 dengan presisi ganda (8 byte) dan f32 dengan presisi tunggal (4 byte). Literal desimal tanpa anotasi menjadi f64 karena presisinya lebih aman untuk perhitungan umum. Nilai f32 cukup ketika kita butuh hemat memori, misalnya menyimpan banyak data pengukuran:

rust
fn main() {
    let berat: f32 = 72.5;
    let tinggi: f64 = 1.78;

    println!("Berat: {} kg", berat);
    println!("Tinggi: {} m", tinggi);
}

Output yang Dihasilkan:

text
Berat: 72.5 kg
Tinggi: 1.78 m

Variable berat memakai f32 karena formulir data kesehatan hanya membutuhkan satu angka di belakang koma, sedangkan tinggi mengikuti bawaan f64. Kedua tipe ini mengikuti standar IEEE 754, sehingga perilakunya sama dengan bilangan desimal di bahasa lain.

Tipe Boolean #

Tipe bool hanya menyimpan dua nilai: true atau false. Tipe ini berukuran 1 byte dan menjadi bahan bakar utama percabangan if yang akan kita pelajari pada section Kontrol Alur:

rust
fn main() {
    let sudah_lunas = true;
    let sudah_dikirim = false;

    println!("Pesanan sudah lunas: {}", sudah_lunas);
    println!("Pesanan sudah dikirim: {}", sudah_dikirim);
}

Output yang Dihasilkan:

text
Pesanan sudah lunas: true
Pesanan sudah dikirim: false

Berbeda dengan PHP, Rust tidak menganggap angka 0 sebagai false atau angka selain nol sebagai true. Nilai boolean hanya bisa dilahirkan dari literal true/false, hasil operator perbandingan, atau operator logika.

Tipe char #

Tipe char menyimpan satu karakter dan selalu berukuran 4 byte karena memakai representasi Unicode Scalar Value. Rentang Unicode yang luas membuat char Rust jauh lebih kaya daripada satu byte ASCII: huruf, angka, simbol mata uang, bahkan emoji semuanya sah:

rust
fn main() {
    let huruf_awal: char = 'B';
    let simbol_mata_uang: char = '₨';
    let emoji_kepiting: char = '🦀';

    println!("Huruf: {}", huruf_awal);
    println!("Simbol: {}", simbol_mata_uang);
    println!("Emoji: {}", emoji_kepiting);
}

Output yang Dihasilkan:

text
Huruf: B
Simbol: ₨
Emoji: 🦀

Perhatikan bahwa literal char ditulis dengan tanda kutip tunggal ('B'), berbeda dengan teks yang lebih panjang yang memakai tanda kutip ganda dan bertipe string. Pembahasan string secara mendalam menunggu section String dan Teks agar kita sudah menguasai ownership terlebih dahulu.

Ringkasan #

Tipe skalair Rust menyimpan satu nilai dengan ukuran memori yang pasti: integer dibagi menjadi signed dan unsigned dengan rentang yang bergantung jumlah byte, floating point tersedia dalam f32 dan f64, boolean menyimpan true atau false, dan char menyimpan satu karakter Unicode berukuran 4 byte. Literal integer dapat ditulis dalam empat sistem bilangan dengan pemisah ribuan berupa garis bawah. Nilai di luar rentang tipe, seperti 300 pada u8, ditolak langsung oleh kompilator. Selanjutnya kita akan mengelompokkan beberapa nilai sekaligus menggunakan tuple dan array.