Types

Rust memiliki beberapa type, entah berupa angka, karakter, dsb. Beberapa type tersebut tergolong sederhana, sedangkan yang lainnya tergolong lebih rumit. Anda juga bisa membuat type Anda sendiri.

Primitive types

See this chapter on YouTube

Rust memiliki type sederhana yang biasanya disebut sebagai primitive types (primitive = paling dasar). Kita akan memulainya dengan integer dan char (karakter). Integer adalah semua angka yang tidak berkoma. Ada 2 type integer:

  • Signed integers (Integer bertanda),
  • Unsigned integers (Integer tidak bertanda).

Bertanda artinya + (tanda tambah) dan - (tanda minus), maka integer bertanda bisa jadi positif atau negatif (contohnya, +8, -8). Namun, integer tidak bertanda hanya dapat menyimpan bilangan bulat positif, karena ia tidak memiliki tanda.

Type-type integer bertanda adalah sebagai berikut: i8, i16, i32, i64, i128, dan isize. Sedangkan ini adalah type-type integer tidak bertanda: u8, u16, u32, u64, u128, dan usize.

Angka setelah i ataupun u adalah panjang bit yang digunakan untuk menyimpan bilangan, jadi semakin besar angkanya, semakin banyak pula bit yang digunakan. 8 bit = 1 byte, jadi i8 adalah 1 byte, i64 adalah 8 byte, dan seterusnya. Type dengan panjang bit yang lebih lebar bisa menyimpan angka yang lebih besar. Contohnya, u8 bisa menyimpan sampai dengan 255, sedangakan u16 bisa menyimpan sampai dengan 65535. Juga u128 bisa menyimpan sampai dengan 340282366920938463463374607431768211455.

Apa itu isize dan usize? Kedua type tersebut menandakan bahwa compiler akan mencocokkan ukuran bit dengan arsitektur komputer anda. (Jumlah bit pada komputer Anda biasanya disebut sebagai arsitektur.) Jadinya isize dan usize pada komputer 32-bit adalah i32 dan u32, juga isize dan usize pada komputer 64-bit adalah i64 dan u64.

Ada banyak alasan mengapa ada banyak sekali type dari integer. Salah satunya adalah performa: angka yang menggunakan byte yang kecil lebih cepat untuk diproses. Contohnya, angka -10 pada i8 representasi binernya adalah 11110110, namun pada i128 representasi binernya adalah 11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110110. Selain itu, ada juga beberapa kegunaan lainnya, seperti:

Karakter di Rust disebut sebagai char. Setiap char memiliki angka: huruf A memiliki nilai 65, sedangkan karakter 友 ("kawan" dalam Bahasa Mandarin) memiliki nilai 21451. Angka-angka ini disebut sebagai "Unicode". Unicode yang menggunakan angka yang lebih kecil diperuntukkan bagi karakter yang sering digunakan, seperti A sampai Z, atau digit 0 sampai 9, maupun spasi.

fn main() {
    let first_letter = 'A';
    let space = ' '; // Sebuah spasi di antara ' ' juga adalah sebuah char
    let other_language_char = 'Ꮔ'; // Berkat Unicode, bahasa lain seperti Cherokee juga tampil dengan baik
    let cat_face = '😺'; // Emojis juga adalah char
}

Karakter yang seringkali digunakan tersebut kebanyakan memiliki nilai dibawah dari 256. Maka, karakter-karakter tersebut muat jikalau disimpan didalam u8. Ingat, u8 bisa menyimpan dari 0 sampai dengan 255, yang artinya, totalnya adalah 256. Ini berarti bahwa Rust bisa dengan aman melakukan cast dari u8 ke char, menggunakan as. ("Cast u8 ke char" artinya "perlakukan u8 sebagai char")

Melakukan Cast menggunakan as sangatlah berguna karena Rust benar-benar sangat ketat. Rust selalu perlu untuk mengetahui type yang digunakan dan tidak akan membiarkan kita menggunakan tipe data yang berbeda meskipun keduanya berupa integer. Pada contoh dibawah ini, code ini tidak akan berjalan:

fn main() { // main() adalah dimana program Rust mulai berjalan. Sedangkan codenya dituliskan di dalam {} (curly brackets)

    let my_number = 100; // Kita tidak menuliskan type dari integer tersebut,
                         // sehingga Rust memilih i32. Rust selalu
                         // memilih i32 untuk integer apabila kita tidak
                         // memberitahukan compiler untuk menggunakan type yang berbeda.

    println!("{}", my_number as char); // ⚠️
}

Alasannya adalah sebagai berikut:

error[E0604]: only `u8` can be cast as `char`, not `i32`
 --> src\main.rs:3:20
  |
3 |     println!("{}", my_number as char);
  |                    ^^^^^^^^^^^^^^^^^

Untungnya, kita bisa dengan mudah memperbaiki ini dengan menggunakan as. Kita tidak bisa melakukan cast i32 sebagai char, tapi kita bisa cast i32 sebagai u8. Dan karena itu, kita bisa melakukan yang sama pada u8 ke char. Maka kita menggunakan as untuk membuat my_number menjadi u8, dan di baris baru, kemudian mengubahnya lagi menjadi char. Jalankan code ini:

fn main() {
    let my_number = 100;
    println!("{}", my_number as u8 as char);
}

Dan ia akan mencetak d, karena itu merupakan char yang bernilai 100.

Cara yang lebih mudah adalah, kita beri tahu saja ke Rust bahwa my_number itu adalah u8. Begini caranya:

fn main() {
    let my_number: u8 = 100; //  ubah my_number ke my_number: u8
    println!("{}", my_number as char);
}

Itu merupakan 2 alasan mengapa ada banyak sekali type integer di Rust. Ini adalah alasan lainnya: usize adalah type yang digunakan Rust untuk keperluan indexing. (Indexing artinya "yang mana item yang pertama", "yang mana item yang kedua", dan seterusnya.) usize adalah type yang cocok untuk melakukan indexing, karena:

  • Sebuah index tidak bisa negatif, jadi yang diperlukan adalah bilangan tidak bertanda (u)
  • Integer yang digunakan harus berukuran besar, karena terkadang kita perlu untuk meng-index banyak hal, tapi
  • u64 tidak bisa digunakan dikarenakan komputer 32-bit tidak bisa menggunakan u64.

Jadi Rust menggunakan usize dan menyerahkan pada komputer kita untuk menentukan integer terbesar yang mampu dijangkau olehnya.

Mari kita pelajari lebih lanjut tentang char. Dapat dilihat bahwa char selalu berisi hanya 1 karakter, dan menggunakan '', bukan "".

Semua chars menggunakan 4 byte memori, karena sejauh ini 4 bytes cukup untuk menampung hampir semua karakter apapun yang ada sekarang:

  • Huruf-huruf dasar dan simbol biasanya memerlukan 1 dari 4 byte: a b 1 2 + - = $ @
  • Aksara lainnya, seperti German Umlaut, memerlukan 2 dari 4 byte: Γ€ ΓΆ ΓΌ ß Γ¨ Γ© Γ  Γ±
  • Aksara Korea, Jepang atau Mandarin memerlukan 3 atau 4 byte: ε›½ μ•ˆ λ…•

Di saat menggunakan karakter sebagai bagian dari sebuah string, maka string akan di-encode untuk menggunakan sesedikit mungkin memori yang dibutuhkan untuk setiap karakter.

Kita bisa menggunakan .len() untuk melihat ini:

fn main() {
    println!("Size of a char: {}", std::mem::size_of::<char>()); // 4 bytes
    println!("Size of string containing 'a': {}", "a".len()); // .len() memberikan ukuran string dalam satuan byte
    println!("Size of string containing 'ß': {}", "ß".len());
    println!("Size of string containing 'ε›½': {}", "ε›½".len());
    println!("Size of string containing 'π“…±': {}", "π“…±".len());
}

Program ini akan mencetak:

Size of a char: 4
Size of string containing 'a': 1
Size of string containing 'ß': 2
Size of string containing 'ε›½': 3
Size of string containing 'π“…±': 4

Anda bisa melihat bahwa huruf a memerlukan 1 byte, aksara Jerman ß memerlukan 2 byte, aksara Jepang ε›½ memerlukan 3 byte, dan aksara Mesir kuno π“…± memerlukan 4 byte.

fn main() {
    let slice = "Hello!";
    println!("Slice is {} bytes.", slice.len());
    let slice2 = "μ•ˆλ…•!"; // Korean for "hi"
    println!("Slice2 is {} bytes.", slice2.len());
}

Program ini akan mencetak:

Slice is 6 bytes.
Slice2 is 7 bytes.

slice memiliki panjang 6 karakter dan memerlukan 6 byte, namun slice2 memiliki panjang 3 karakter dan memerlukan 7 byte.

Jika .len() memberikan informasi tentang size dalam satuan byte, bagaimana tentang ukuran dalam satuan panjang karakter? Kita akan mempelajari tentang method ini nanti, tapi Anda bisa mengingat bahwa method .chars().count() bisa digunakan untuk keperluan tersebut. .chars().count() merubah apa yang kita tulis menjadi karakter dan menghitung berapa banyak karakter yang terdapat dalam tulisan tersebut.

fn main() {
    let slice = "Hello!";
    println!("Slice is {} bytes and also {} characters.", slice.len(), slice.chars().count());
    let slice2 = "μ•ˆλ…•!";
    println!("Slice2 is {} bytes but only {} characters.", slice2.len(), slice2.chars().count());
}

Program ini akan mencetak:

Slice is 6 bytes and also 6 characters.
Slice2 is 7 bytes but only 3 characters.