unisbadri.com » Python Java Golang Typescript Kotlin Ruby Rust Dart PHP
Interface

Interface #

Di Rust, konsep yang setara dengan interface di bahasa pemrograman lain (seperti Java atau C#) adalah traits. Traits mendefinisikan serangkaian metode yang dapat diimplementasikan oleh tipe tertentu. Traits memungkinkan Anda untuk menentukan perilaku bersama yang dapat digunakan oleh berbagai tipe tanpa harus membuat hirarki kelas, seperti yang dilakukan dalam paradigma berbasis objek.

Pengertian Traits #

Trait adalah kumpulan metode yang bisa dimiliki oleh tipe tertentu. Ketika suatu tipe mengimplementasikan trait, tipe tersebut wajib menyediakan definisi untuk metode-metode yang ada dalam trait tersebut. Traits digunakan untuk menetapkan kontrak tentang apa yang bisa dilakukan oleh suatu tipe, tanpa mempermasalahkan bagaimana tipe tersebut mengimplementasikan kontrak tersebut.

Definisi Trait:

trait NamaTrait {
    fn nama_metode(&self);
}

Contoh Sederhana:

trait Greet {
    fn say_hello(&self);
}

struct Person {
    name: String,
}

impl Greet for Person {
    fn say_hello(&self) {
        println!("Hello, my name is {}!", self.name);
    }
}

fn main() {
    let person = Person { name: String::from("Alice") };
    person.say_hello();
}

Penjelasan:

  • Greet adalah trait yang mendefinisikan metode say_hello.
  • Person adalah struktur yang mengimplementasikan trait Greet.
  • Ketika kita memanggil say_hello pada instance Person, implementasi dari trait Greet digunakan.

Traits dengan Default Method #

Rust memungkinkan Anda untuk memberikan implementasi default untuk metode di dalam trait. Ini berarti bahwa tipe yang mengimplementasikan trait tersebut tidak wajib untuk mendefinisikan ulang metode yang sudah memiliki default implementation, kecuali mereka ingin menggantinya.

Contoh Traits dengan Default Method:

trait Greet {
    fn say_hello(&self) {
        println!("Hello, world!");
    }
}

struct Person;

impl Greet for Person {}

fn main() {
    let person = Person;
    person.say_hello(); // Menggunakan default method
}

Penjelasan:

  • say_hello memiliki implementasi default yang mencetak “Hello, world!”.
  • Person mengimplementasikan Greet tanpa mendefinisikan ulang say_hello, sehingga default method digunakan.

Menggabungkan Traits #

Rust memungkinkan Anda untuk mendefinisikan sebuah trait yang menggabungkan beberapa trait lainnya, yang disebut sebagai supertrait. Ini berguna ketika Anda ingin memastikan bahwa tipe yang mengimplementasikan trait juga mengimplementasikan trait lain yang terkait.

Contoh Penggabungan Traits:

trait Human {
    fn speak(&self);
}

trait Greet: Human {
    fn greet(&self) {
        self.speak();
        println!("Nice to meet you!");
    }
}

struct Person;

impl Human for Person {
    fn speak(&self) {
        println!("Hello!");
    }
}

impl Greet for Person {}

fn main() {
    let person = Person;
    person.greet();
}

Penjelasan:

  • Greet adalah supertrait dari Human, yang berarti setiap tipe yang mengimplementasikan Greet harus juga mengimplementasikan Human.
  • Person mengimplementasikan Human, sehingga dapat mengimplementasikan Greet.

Trait Bounds pada Generics #

Rust memungkinkan Anda untuk menggunakan traits sebagai batasan (bounds) pada tipe generik. Ini memastikan bahwa tipe generik hanya dapat digunakan jika mereka mengimplementasikan trait tertentu.

Contoh Trait Bounds pada Generics:

trait Summarize {
    fn summarize(&self) -> String;
}

fn notify<T: Summarize>(item: T) {
    println!("Summary: {}", item.summarize());
}

struct NewsArticle {
    headline: String,
    content: String,
}

impl Summarize for NewsArticle {
    fn summarize(&self) -> String {
        format!("{}: {}", self.headline, self.content)
    }
}

fn main() {
    let article = NewsArticle {
        headline: String::from("New Rust Release!"),
        content: String::from("Rust 1.56 has been released."),
    };

    notify(article);
}

Penjelasan:

  • Summarize adalah trait yang mendefinisikan metode summarize.
  • Fungsi generik notify hanya menerima tipe yang mengimplementasikan Summarize.
  • NewsArticle mengimplementasikan Summarize, sehingga dapat diteruskan ke notify.

Implementasi Trait untuk Tipe yang Ada #

Rust memungkinkan Anda untuk mengimplementasikan trait untuk tipe yang sudah ada, termasuk tipe-tipe bawaan Rust dan tipe dari crate eksternal. Namun, Anda tidak bisa mengimplementasikan trait untuk tipe yang ada jika trait dan tipe tersebut keduanya tidak didefinisikan di dalam crate yang sama (ini disebut Orphan Rule).

Contoh Implementasi Trait untuk Tipe yang Ada:

trait Double {
    fn double(&self) -> i32;
}

impl Double for i32 {
    fn double(&self) -> i32 {
        self * 2
    }
}

fn main() {
    let x = 5;
    println!("Double of {} is {}", x, x.double());
}

Penjelasan:

  • Double adalah trait yang menyediakan metode double.
  • Trait ini diimplementasikan untuk tipe i32, sehingga semua nilai i32 memiliki metode double.

Trait Objects (Objek Trait) #

Rust mendukung dynamic dispatch melalui penggunaan trait objects. Trait objects memungkinkan Anda untuk menampung berbagai tipe yang mengimplementasikan trait tertentu dalam satu variabel atau struktur. Trait objects berguna ketika Anda ingin menggunakan polimorfisme.

Sintaks Trait Objects:

fn example_function(object: &dyn TraitName) {
    // Blok kode
}

Contoh Trait Objects:

trait Draw {
    fn draw(&self);
}

struct Circle;
struct Square;

impl Draw for Circle {
    fn draw(&self) {
        println!("Drawing a circle");
    }
}

impl Draw for Square {
    fn draw(&self) {
        println!("Drawing a square");
    }
}

fn main() {
    let shapes: Vec<&dyn Draw> = vec![&Circle, &Square];

    for shape in shapes {
        shape.draw();
    }
}

Penjelasan:

  • Draw adalah trait yang mendefinisikan metode draw.
  • Circle dan Square adalah struktur yang mengimplementasikan Draw.
  • shapes adalah vektor dari trait objects (&dyn Draw), yang dapat menampung referensi ke berbagai tipe yang mengimplementasikan Draw.
  • Polimorfisme tercapai karena draw dapat dipanggil pada elemen shapes tanpa mengetahui tipe konkret dari elemen tersebut.

impl Trait dan Return Types #

Rust menyediakan cara singkat untuk menentukan bahwa fungsi mengembalikan tipe yang mengimplementasikan trait tertentu menggunakan impl Trait. Ini sangat berguna ketika Anda ingin menyembunyikan tipe konkret dari nilai yang dikembalikan.

Contoh impl Trait:

trait Summarize {
    fn summarize(&self) -> String;
}

struct NewsArticle {
    headline: String,
    content: String,
}

impl Summarize for NewsArticle {
    fn summarize(&self) -> String {
        format!("{}: {}", self.headline, self.content)
    }
}

fn returns_summarizable() -> impl Summarize {
    NewsArticle {
        headline: String::from("Breaking News!"),
        content: String::from("Something important happened."),
    }
}

fn main() {
    let article = returns_summarizable();
    println!("Summary: {}", article.summarize());
}

Penjelasan:

  • Fungsi returns_summarizable mengembalikan nilai dengan tipe yang tidak diketahui oleh pemanggil, kecuali bahwa nilai tersebut mengimplementasikan Summarize.
  • impl Trait menyederhanakan deklarasi tipe kembalian ketika tipe konkret tidak penting bagi pemanggil.

Blanket Implementations #

Blanket Implementations adalah implementasi trait untuk semua tipe yang memenuhi syarat tertentu. Ini sangat berguna untuk mendefinisikan perilaku umum bagi semua tipe yang mengimplementasikan suatu trait lain.

Contoh Blanket Implementations:

trait Printable {
    fn print(&self);
}

impl<T: std::fmt::Debug> Printable for T {
    fn print(&self) {
        println!("{:?}", self);
    }
}

fn main() {
    let x = 42;
    let y = "Hello";

    x.print();
    y.print();
}

Penjelasan:

  • Printable adalah trait yang mendefinisikan metode print.
  • Blanket implementation ini membuat semua tipe yang mengimplementasikan Debug secara otomatis mengimplementasikan Printable.
  • Nilai x dan y dapat dicetak menggunakan metode print karena mereka mengimplementasikan Debug.

Kesimpulan #

Traits di Rust adalah mekanisme yang kuat untuk mendefinisikan dan mengimplementasikan perilaku bersama di berbagai tipe. Dengan traits, Anda dapat menghindari beberapa kelemahan dari inheritance (pewarisan) tradisional yang ditemukan di bahasa lain, dan malah fokus pada komposisi dan penggunaan perilaku yang di-share.

Traits memungkinkan Rust untuk memberikan konsep polimorfisme dan abstraksi tanpa kehilangan keamanan tipe atau performa. Memahami traits dan cara penggunaannya akan membantu Anda menulis kode Rust yang lebih fleksibel, modular, dan dapat digunakan kembali.

« Kelas
Eksepsi »