Trait#

Trait#

Trait pada bahasa Rust merupakan sebuah cara kita mencapai polymorphism. Dengan trait, kita dapat mendefiniskan perlakuan yang sama, untuk tipe-tipe yang berbeda. Trait pada Rust mirip dengan interface pada bahasa pemrograman lainnya, namun lebih powerful.

Pada trait, kita dapat mendefinisikan method-method yang harus diimplementasikan oleh sebuah tipe, kemudian memanggil method-method tersebut pada tipe tersebut. Sebagai contoh, kita dapat mendefinisikan sebuah trait Animal yang memiliki method make_sound(), kemudian kita dapat membuat sebuah struct Cat yang mengimplementasikan Animal, dan kita dapat memanggil method make_sound() pada Cat.

Kita akan mencoba membuat struct Cat yang mengimplementasikan trait Animal. Pertama, kita akan mendefinisikan trait Animal dan struct Cat terlebih dahulu.

trait Animal {
    fn make_sound(&self);
}

struct Cat {
    name: String,
}

Lalu kita akan mengimplementasikan trait Animal untuk struct Cat.

impl Animal for Cat {
    fn make_sound(&self) {
        println!("Meow!");
    }
}

Dan terakhir, kita akan membuat sebuah instance dari struct Cat dan memanggil method make_sound() pada instance tersebut.

fn main() {
    let cat = Cat { name: String::from("Bacing") };
    cat.make_sound();
}

Dengan begini, kita telah berhasil membuat sebuah trait dan mengimplementasikannya pada sebuah struct. Method make_sound() pada trait Animal dapat kita panggil pada instance dari struct Cat.

Kalian pasti bertanya-tanya, kenapa kita tidak langsung saja membuat method make_sound() pada struct Cat? Nah, memang tidak berarti untuk membuat satu trait untuk satu tipe saja. Karena itu, kita akan membuat satu tipe lagi yang akan mengimplementasikan Animal.

struct Dog {
    name: String,
}

impl Animal for Dog {
    fn make_sound(&self) {
        println!("Woof!");
    }
}

Diatas, kita telah membuat struct Dog dan mengimplementasikan Animal untuk Dog. Sekarang, kita akan membuat sebuah fungsi yang menerima sebuah tipe yang mengimplementasikan Animal sebagai parameter.

fn make_animal_sound(animal: &dyn Animal) {
    animal.make_sound();
}

Kita akan membuat sebuah instance dari struct Cat dan Dog, kemudian kita akan memanggil fungsi make_animal_sound() pada kedua instance tersebut.

fn main() {
    let cat = Cat { name: String::from("Bacing") };
    let dog = Dog { name: String::from("Baguk") };

    make_animal_sound(&cat);
    make_animal_sound(&dog);
}

Dan hasilnya akan dikeluarkan sesuai dengan masing-masing implementasi dari method make_sound. Untuk misalnya fungsi yang melakukan hal yang sama untuk banyak tipe, kita tidak perlu membuat banyak fungsi untuk masing-masing tipe. Cukup satu dimana perbedaan yang muncul akan tergantung dengan implementasi didalam tipe itu sendiri.

Keyword dyn diatas harus digunakan bila kita memakai trait sebagai parameter. Namun, ada cara yang lebih baik selain menggunakan keyword dyn terutama bila kita menginginkan untuk memakai lebih dari satu trait, yaitu dengan generics.

fn make_animal_sound<T: Animal>(animal: T) {
    animal.make_sound();
}

Yang mana akan kita bahas dengan lebih lanjut di bagian advanced.

# LATIHAN

// TODO