References and the dot operator

Kita telah mempelajari bahwa di saat kita memiliki sebuah reference, Anda perlu menggunakan * untuk mendapatkan valuenya. Sebuah reference adalah type yang berbeda dari variabel aslinya, sehingga hal yang dicontohkan seperti hal di bawah ini tidak akan berjalan:

fn main() {
    let my_number = 9;
    let reference = &my_number;

    println!("{}", my_number == reference); // ⚠️
}

Dan compiler akan menyampaikan pesan berikut:

error[E0277]: can't compare `{integer}` with `&{integer}`
 --> src\main.rs:5:30
  |
5 |     println!("{}", my_number == reference);
  |                              ^^ no implementation for `{integer} == &{integer}`

Sehingga kita mengganti line 5 menjadi println!("{}", my_number == *reference); dan sekarang outputnya adalah true karena kondisinya sekarang adalah i32 == i32, bukan i32 == &i32. Hal seperti ini biasa disebut sebagai dereferencing.

Namun di saat Anda menggunakan method, Rust akan melakukan dereference secara otomatis. Tanda . pada method biasa disebut sebagai dot operator, dan ia akan langsung melakukan dereferencing.

Pertama, kita buat sebuah struct dengan 1 field bertype u8. Kemudian kita akan membuat sebuah reference ke field yang berada di dalam struct tersebut, dan mencoba untuk meng-comparenya (membandingkannya/mengkomparasinya). Dari contoh yang sebelumnya, bisa prediksikan bahwa hasilnya akan gagal :

struct Item {
    number: u8,
}

fn main() {
    let item = Item {
        number: 8,
    };

    let reference_number = &item.number; //  type dari reference_number adalah &u8

    println!("{}", reference_number == 8); // ⚠️ &u8 dan u8 tidak bisa dikomparasi
}

Untuk membuatnya bekerja, kita perlu untuk melakukan dereference: println!("{}", *reference_number == 8);.

Namun dengan menggunakan dot operator, kita tidak lagi perlu untuk menggunakan *. Contohnya:

struct Item {
    number: u8,
}

fn main() {
    let item = Item {
        number: 8,
    };

    let reference_item = &item;

    println!("{}", reference_item.number == 8); // kita tidak perlu untuk menuliskan *reference_item.number
}

Sekarang kita akan membuat sebuah method untuk Item yang membandingkan number dengan angka lainnya. Kita sama sekali tidak memerlukan * dimanapun:

struct Item {
    number: u8,
}

impl Item {
    fn compare_number(&self, other_number: u8) { // ambil reference dari self
        println!("Are {} and {} equal? {}", self.number, other_number, self.number == other_number);
            // Kita tidak perlu lagi menuliskan *self.number
    }
}

fn main() {
    let item = Item {
        number: 8,
    };

    let reference_item = &item; // ini bertype &Item
    let reference_item_two = &reference_item; // ini bertype &&Item

    item.compare_number(8); // method ini bisa berjalan
    reference_item.compare_number(8); // method yang ini juga berjalan
    reference_item_two.compare_number(8); // dan begitu pula pada bagian ini

}

Jadi, cukup ingat hal ini: di saat Anda menggunakn . (dot operator), Anda tidak lagi perlu mengkhawatirkan tentang *.