External crates

External crate artinya adalah "crate yang dibuat oleh orang lain".

Pada bagian ini, Anda hampir perlu menginstall Rust, namun kita masih bisa cukup menggunakan Playground. Sekarang kita akan mempelajari bagaimana cara melakukan import crates yang dibuat oleh orang lain. Ini sangatlah penting di Rust karena dua alasan:

  • Sangatlah mudah untuk mengimport crates, dan
  • Standard library yang dimiliki oleh Rust sangatlah kecil.

Artinya, adalah normal di Rust untuk memasukkan sebuah external crate untuk menggunakan beberapa basic function. Jika kita bisa dengan mudah menggunakan external crates, maka Anda bisa memilih crate yang paling terbaik. Mungkin seseorang akan membuat sebuah crate untuk sebuah function, dan kemudian ada orang lain yang membuat function serupa yang lebih baik.

Di buku ini, kita hanya akan mengulas crates yang paling populer, crates yang mana semua orang yang menggunakan Rust tahu.

Untuk mulai mempelajari external crates, kita akan mulai dengan crate yang paling umum digunakan: rand.

rand

Apakah Anda menyadari sejauh ini kita belum ada menggunakan angka random? Itu dikarenakan angka random tidak berada di dalam standard library. Tapi ada banyak crate yang "hampir menjadi standard library" karena semua orang menggunakannya. Bagaimanapun, adalah hal yang mudah untuk memasukkan dan menggunakan sebuah crate. Jika Anda memiliki Rust pada komputer Anda, ada file yang bernama Cargo.toml yang menyimpan informasi tentang crate. File Cargo.toml terlihat seperti ini pada awalnya:

[package]
name = "rust_book"
version = "0.1.0"
authors = ["David MacLeod"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]

Sekarang, jika Anda ingin menambahkan crate rand, cari cratenya di crates.io, yang mana adalah tempat semua crate dipost. Ia akan mengarahkanmu ke https://crates.io/crates/rand. Dan di saat Anda mengkliknya, Anda bisa melihat screen yang bertuliskan Cargo.toml rand = "0.7.3". Yang perlu Anda lakukan adalah menambahkannya setelah [dependencies], seperti ini:

[package]
name = "rust_book"
version = "0.1.0"
authors = ["David MacLeod"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
rand = "0.7.3"

Dan Cargo akan melakukan sisanya untuk Anda. Kemudian Anda bisa mulai menulis code seperti code contoh yang ada pada website dokumentasi rand. Untuk mendapatkan dokumentasinya Anda bisa klik pada tombol docs pada laman di crates.io.

Jadi cukup berbicara tentang Cargo: kita tetap menggunakan Playground. Beruntungnya, Playground telah terinstall 100 crate teratas (yang sering digunakan). Sehingga Anda tidak perlu menuliskannya di dalam Cargo.toml. Pada Playground Anda bisa membayangkan bahwa ia memiliki list yang panjang (seperti di bawah ini) dengan 100 crate terdaftar di dalamnya:

[dependencies]
rand = "0.7.3"
some_other_crate = "0.1.0"
another_nice_crate = "1.7"

Itu berarti bahwa untuk menggunakan rand, Anda bisa melakukan hal seperti ini.

use rand; // Ini berarti seluruh dari crate rand
          // Pada komputer Anda, Anda tidak bisa langsung menulisnya seperti ini;
          // Anda perlu menuliskannya di dalam file Cargo.toml terlebih dahulu

fn main() {
    for _ in 0..5 {
        let random_u16 = rand::random::<u16>();
        print!("{} ", random_u16);
    }
}

Ia akan mencetak angka u16 yang berbeda setiap saat, seperti 42266 52873 56528 46927 6867.

Function utama yang ada di dalam rand adalah random dan thread_rng (rng artinya "random number generator"). Dan sebenarnya jika Anda melihat pada random, ia mengatakan: "Ini sebenarnya adalah shortcut untuk thread_rng().gen()". Jadi ia sebenarnya hanyalah thread_rng yang melakukan hampir semua hal.

Ini adalah contoh sederhana angka random dari angka 1 sampai dengan 10. Untuk mendapatkan angka tersebut, kita menggunakan .gen_range() di antara 1 dan 11.

use rand::{thread_rng, Rng}; // atau cukup use rand::*; jika kita cukup malas untuk menuliskannya

fn main() {
    let mut number_maker = thread_rng();
    for _ in 0..5 {
        print!("{} ", number_maker.gen_range(1, 11));
    }
}

Hasil cetaknya kira-kira seperti ini: 7 2 4 8 6.

Dengan angka random kita bisa membuat hal-hal menarik seperti membuat karakter untuk game. Kita akan menggunakan rand dan hal lain yang kita ketahui untuk membuatnya. Pada game ini, karakter kita memiliki enam stats, dan Anda menggunakan d6 untuk stats tersebut. d6 adalah dadu yang memberikan 1, 2, 3, 4, 5, atau 6 di saat Anda melemparkannya. Setiap karakter melempar d6 tiga kali, jadinya setiap stat nilainya berada di antara 3 dan 18.

Tapi terkadang tidak adil jika karakter kita mendapatkan nilai yang rendah seperti 3 atau 4. Misalnya, jika strength Anda hanya bernilai 3, Anda tidak bisa melakukan apapun. Jadi ada satu method dimana akan menggunakan d6 empat kali. Anda lempar dadunya empat kali, dan buang angka yang paling rendah. Sehingga, jika Anda mendapatkan 3, 3, 1, 6 maka Anda bisa simpan 3, 3, 6 = 12. Kita akan membuat method ini juga sehingga si pemain game bisa menentukannya.

Ini adalah character creator sederhana yang kita buat. Kita membuat sebuah struct Character untuk statnya, dan bahkan mengimplementasikan Display untuk mencetak statnya dengan cara yang kita inginkan.

use rand::{thread_rng, Rng}; // Atau cukup tuliskan use rand::*; jika kita malas menuliskannya
use std::fmt; // digunakan di impl Display untuk character yang kita buat


struct Character {
    strength: u8,
    dexterity: u8,    // ini adalah "kecepatan"
    constitution: u8, // ini adalah "health"
    intelligence: u8,
    wisdom: u8,
    charisma: u8, // Ini adalah "popularitas karakter"
}

fn three_die_six() -> u8 { // "die" adalahsesuatu yang kita lempar untuk mendapatkan angkanya
    let mut generator = thread_rng(); // buat random number generator
    let mut stat = 0; // ini adalah totalnya
    for _ in 0..3 {
        stat += generator.gen_range(1..=6); // jumlahkan setiap dilempar
    }
    stat // Return totalnya
}

fn four_die_six() -> u8 {
    let mut generator = thread_rng();
    let mut results = vec![]; // pertama-tama, taruh angka-angkanya ke dalam vec
    for _ in 0..4 {
        results.push(generator.gen_range(1..=6));
    }
    results.sort(); // sekarang yang hasilnya seperti [4, 3, 2, 6] akan menjadi [2, 3, 4, 6]
    results.remove(0); // sekarang ia menjadi [3, 4, 6]
    results.iter().sum() // Return resultnya
}

enum Dice {
    Three,
    Four
}

impl Character {
    fn new(dice: Dice) -> Self { // true untuk tiga dadu, false untuk empat dadu
        match dice {
            Dice::Three => Self {
                strength: three_die_six(),
                dexterity: three_die_six(),
                constitution: three_die_six(),
                intelligence: three_die_six(),
                wisdom: three_die_six(),
                charisma: three_die_six(),
            },
            Dice::Four => Self {
                strength: four_die_six(),
                dexterity: four_die_six(),
                constitution: four_die_six(),
                intelligence: four_die_six(),
                wisdom: four_die_six(),
                charisma: four_die_six(),
            },
        }
    }
    fn display(&self) { // Kita bisa melakukan ini karena kita mengimplementasikan Display di bawah
        println!("{}", self);
        println!();
    }
}

impl fmt::Display for Character { // Cukup ikuti code di https://doc.rust-lang.org/std/fmt/trait.Display.html dan ubah sedikit saja
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "Your character has these stats:
strength: {}
dexterity: {}
constitution: {}
intelligence: {}
wisdom: {}
charisma: {}",
            self.strength,
            self.dexterity,
            self.constitution,
            self.intelligence,
            self.wisdom,
            self.charisma
        )
    }
}



fn main() {
    let weak_billy = Character::new(Dice::Three);
    let strong_billy = Character::new(Dice::Four);
    weak_billy.display();
    strong_billy.display();
}

Hasil cetaknya adalah seperti berikut:

Your character has these stats:
strength: 9
dexterity: 15
constitution: 15
intelligence: 8
wisdom: 11
charisma: 9

Your character has these stats:
strength: 9
dexterity: 13
constitution: 14
intelligence: 16
wisdom: 16
charisma: 10

Karakter yang dibuat dengan empat dadu biasanya sedikit lebih baik dalam banyak hal (statnya).

rayon

rayon adalah crate popular yang memungkinkan Anda mempercepat code Rust yang Anda buat. Ia popular karena ia akan membuat threads tanpa perlu menuliskan thread::spawn. Dengan kata lain, ia popular karena ia sangat efektif namun mudah untuk ditulis. Contohnya:

  • .iter(), .iter_mut(), into_iter() di rayon ditulis seperti ini:
  • .par_iter(), .par_iter_mut(), par_into_iter(). Jadi Anda cukup menambahkan par_ dan code Anda menjadi lebih cepat. (par artinya "parallel")

Method lain yang juga sama: .chars() diganti dengan .par_chars(), dan seterusnya.

Ini adalah contoh sederhana dari sebuah potongan code yang membuat komputer bekerja lebih berat:

fn main() {
    let mut my_vec = vec![0; 200_000];
    my_vec.iter_mut().enumerate().for_each(|(index, number)| *number+=index+1);
    println!("{:?}", &my_vec[5000..5005]);
}

Ia membuat sebuah vector dengan 200,000 item di dalamnya: semua item tersebut adalah 0. Kemudian kita panggil .enumerate() untuk mendapatkan index setiap angka, dan mengubah setiap 0 menjadi angka indexnya. Tentunya terlalu panjang jika kita cetak semuanya, sehingga kita hanya cetak item ke-5000 sampai dengan item ke-5004. Hal ini tetaplah cepat dilakukan Rust, namun jika Anda ingin Anda bisa membuatnya lebih cepat dengan menggunakan Rayon. Codenya hampir sama:

use rayon::prelude::*; // Import rayon

fn main() {
    let mut my_vec = vec![0; 200_000];
    my_vec.par_iter_mut().enumerate().for_each(|(index, number)| *number+=index+1); // tambahkan par_ ke iter_mut
    println!("{:?}", &my_vec[5000..5005]);
}

Dan seperti itulah cara penggunaannya. rayon memiliki method lainnya untuk mengkostumisasi apa yang ingin kita lakukan, tetapi yang paling sederhana, cukup "tambahkan _par untuk membuat program Anda menjadi lebih cepat".

serde

serde adalah crate yang populer yang memungkinkan Anda untuk mengkonversi ke dan dari format seperti JSON, YAML, dll. Cara paling umum untuk menggunakannya adalah dengan membuat struct dengan dua buah attribute di atasnya. Ia terlihat seperti ini:


#![allow(unused)]
fn main() {
#[derive(Serialize, Deserialize, Debug)]
struct Point {
    x: i32,
    y: i32,
}
}

Trait Serialize dan Deserialize adalah trait yang membuat konversinya menjadi lebih mudah. (Hal ini pulalah yang menjadi asal-muasal dari nama serde) Jika Anda memiliki trait tersebut pada struct Anda, maka Anda bisa memanggil method untuk mengubahnya dari/menjadi JSON, atau format lainnya.

regex

Crate regex memungkinkan Anda melakukan pencarian melalui text menggunakan regular expressions. Dengan ini Anda bisa mencocokkan sesuatu text seperti colour, color, colours dan colors hanya dengan satu pencarian. Regular expressions adalah bahasa lain yang harus Anda pelajari jika Anda ingin menggunakannya.

chrono

chrono adalah crate yang biasanya digunakan oleh programmer yang perlu berurusan dengan function-function waktu. Kita melihat standard library yang sekarang telah banyak memiliki function-function yang berhubungan dengan waktu, tapi jika Anda memerlukan lebih dari sekedar function standard, chrono adalah crate yang bagus untuk digunakan.