Type aliases

Type alias artinya "memberikan nama baru ke type lain". Type alias bisa dilakukan dengan sangat mudah. Biasanya Anda menggunakannya di saat Anda mempunya type yang panjang dan tidak ingin untuk menulisnya setiap saat. Ini juga bagus saat Anda ini memberikan nama yang lebih baik pada sebuah type yang mana membuatnya menjadi lebih mudah untuk diingat. Ini adalah dua contoh penggunaan type alias.

Ini adalah type yang tidak begitu rumi, namun Anda ingin membuat codenya menjadi mudah untuk dipahami oleh orang lain (dan juga untuk diri kita sendiri):

type CharacterVec = Vec<char>;

fn main() {}

Ini adalah type yang sulit untuk dibaca:

// type yang dikembalikan benar-benar sangat panjang
fn returns<'a>(input: &'a Vec<char>) -> std::iter::Take<std::iter::Skip<std::slice::Iter<'a, char>>> {
    input.iter().skip(4).take(5)
}

fn main() {}

Sehingga Anda bisa mengubahnya menjadi seperti ini:

type SkipFourTakeFive<'a> = std::iter::Take<std::iter::Skip<std::slice::Iter<'a, char>>>;

fn returns<'a>(input: &'a Vec<char>) -> SkipFourTakeFive {
    input.iter().skip(4).take(5)
}

fn main() {}

Tentu saja, Anda juga bisa melakukan import itemnya untuk menuliskan typenya menjadi lebih pendek:

use std::iter::{Take, Skip};
use std::slice::Iter;

fn returns<'a>(input: &'a Vec<char>) -> Take<Skip<Iter<'a, char>>> {
    input.iter().skip(4).take(5)
}

fn main() {}

Sehingga Anda bisa menentukan yang mana yang terlihat lebih baik pada code Anda, tergantung dari yang Anda suka.

Perlu diingat bahwa cara ini tidaklah membuat sebuah type yang baru. Ia hanyalah sebuah nama yang digunakan sebagai wakil dari type yang sebenarnya. Jadi jika Anda menulis type File = String;, compiler hanya melihatnya sebagai String. Sehingga program dibawah ini akan mencetak true:

type File = String;

fn main() {
    let my_file = File::from("I am file contents");
    let my_string = String::from("I am file contents");
    println!("{}", my_file == my_string);
}

Jadi bagaimana jika Anda ingin membuat type baru yang sebenarnya?

Jika Anda ingin membuat type file yang baru yang mana compiler akan melihatnya sebagai File, Anda bisa memasukkannya ke dalam struct:

struct File(String); // File adalah pembungkus String

fn main() {
    let my_file = File(String::from("I am file contents"));
    let my_string = String::from("I am file contents");
}

Namun code di bawah ini tidak berjalan, karena keduanya adalah type yang berbeda:

struct File(String); // File adalah pembungkus String

fn main() {
    let my_file = File(String::from("I am file contents"));
    let my_string = String::from("I am file contents");
    println!("{}", my_file == my_string);  // ⚠️ tidak bisa membandingkan File dengan String
}

Jika Anda ingin membandingkannya dengan String didalamnya, Anda bisa menggunakan my_file.0:

struct File(String);

fn main() {
    let my_file = File(String::from("I am file contents"));
    let my_string = String::from("I am file contents");
    println!("{}", my_file.0 == my_string); // my_file.0 adalah String, sehingga hasilnya adalah true
}

Importing and renaming inside a function

Biasanya Anda menulis use pada bagian atas program, seperti ini:

use std::cell::{Cell, RefCell};

fn main() {}

Tapi kita juga melihat bahwa kita bisa melakukan ini dimana saja, terutama di dalam functions dengan enums yang memiliki nama yang panjang. Ini adalah contohnya.

enum MapDirection {
    North,
    NorthEast,
    East,
    SouthEast,
    South,
    SouthWest,
    West,
    NorthWest,
}

fn main() {}

fn give_direction(direction: &MapDirection) {
    match direction {
        MapDirection::North => println!("You are heading north."),
        MapDirection::NorthEast => println!("You are heading northeast."),
        // Masih banyak yang harus diketik...
        // ⚠️ karena kita tidak menuliskan setiap variantnya
    }
}

Jadi sekarang kita akan meng-import MapDirection ke dalam function. Yang berarti bahwa di dalam function, Anda bisa menuliskannya cukup seperti North dan seterusnya.

enum MapDirection {
    North,
    NorthEast,
    East,
    SouthEast,
    South,
    SouthWest,
    West,
    NorthWest,
}

fn main() {}

fn give_direction(direction: &MapDirection) {
    use MapDirection::*; // Import semua yang ada di dalam MapDirection
    let m = "You are heading";

    match direction {
        North => println!("{} north.", m),
        NorthEast => println!("{} northeast.", m),
        // Ini cara penulisan yang lebih baik
        // ⚠️
    }
}

Kita telah melihat bahwa ::* berarti "import semua yang ada setelah ::". Pada kasus kita, itu berarti adalah North, NorthEast...dan seterusnya sampai dengan NorthWest. Di saat Anda melakukan import terhadap code yang dibuat oleh orang lain, Anda juga bisa melakukannya. Namun bila codenya sangatlah besar, mungkin Anda akan mendapatkan masalah. Bagaimana jika code tersebut memiliki beberapa item yang sama seperti yang ada pada code Anda? Jadi, biasanya jalan terbaiknya adalah dengan tidak selalu menggunakan ::* setiap saat, kecuali Anda yakin. Seringkali Anda melihat bagian yang disebut sebagai prelude pada code orang lain yang berisi semua item utama yang mungkin Anda perlukan. Maka Anda biasanya akan menggunakannya seperti ini: name::prelude::*. Kita akan membicarakan ini lebih lanjut pada bagian tentang modules dan crates.

Anda juga bisa menggunakan as untuk mengganti namanya. Sebagai contoh, mungkin Anda sedang menggunakan code orang lain dan Anda tidak bisa mengganti nama variant yang ada di dalam enum:

enum FileState {
    CannotAccessFile,
    FileOpenedAndReady,
    NoSuchFileExists,
    SimilarFileNameInNextDirectory,
}

fn main() {}

Maka, Anda bisa 1) import semuanya dan 2) mengganti namanya:

enum FileState {
    CannotAccessFile,
    FileOpenedAndReady,
    NoSuchFileExists,
    SimilarFileNameInNextDirectory,
}

fn give_filestate(input: &FileState) {
    use FileState::{
        CannotAccessFile as NoAccess,
        FileOpenedAndReady as Good,
        NoSuchFileExists as NoFile,
        SimilarFileNameInNextDirectory as OtherDirectory
    };
    match input {
        NoAccess => println!("Can't access file."),
        Good => println!("Here is your file"),
        NoFile => println!("Sorry, there is no file by that name."),
        OtherDirectory => println!("Please check the other directory."),
    }
}

fn main() {}

Sehingga sekarang Anda bisa menuliskan OtherDirectory daripada menulisnya dengan FileState::SimilarFileNameInNextDirectory.