Generics & Traits
Versi Bahasa Indonesia. Baca dalam Bahasa Inggris
Dengan menggunakan Generics dan Traits, kita dapat memanfaatkan kekuatan gabungan mereka untuk menulis kode yang efisien, dapat digunakan kembali, dan type-safe.
Generics
- Generics digunakan untuk membuat definisi untuk item seperti function signature atau struct, yang kemudian dapat kita gunakan dengan banyak tipe data konkret yang berbeda.
- Jadi, alih-alih menulis implementasi terpisah untuk setiap tipe, Anda mendefinisikan tipe generik yang dapat diganti dengan tipe konkret saat kode digunakan.
- Direpresentasikan dengan angle brackets (
<>) dan sering dilambangkan denganT,U, atau nama deskriptif lainnya. - Generics di Rust diselesaikan saat compile time, memastikan bahwa tipe yang tidak valid tidak dapat digunakan.
- Rust menghasilkan kode yang dioptimalkan untuk setiap tipe konkret yang digunakan dengan generic pada saat compile, menghindari runtime overhead.
Generic Functions
-
Fungsi dapat menggunakan generics untuk beroperasi pada banyak tipe.
fn hello<T: std::fmt::Display>(name: T) { println!("hello {}!", name); } fn main() { hello(777); hello("World"); }
Generic Structs
-
Struct dapat menyimpan nilai dari tipe apa pun menggunakan generics.
struct Point<T> { x: T, y: T, } let int_point = Point { x: 1, y: 2 }; let float_point = Point { x: 1.7, y: 2.4 };
Generic Enums
-
Enum juga dapat menggunakan generics, seperti yang terlihat pada Option atau Result.
enum Option<T> { Some(T), None, } let int_option: Option<i32> = Option::Some(10); let float_option: Option<f64> = Option::Some(5.7);
Traits
- Trait mirip dengan interface di bahasa pemrograman lain.
- Ia mendefinisikan sekumpulan metode yang harus diimplementasikan oleh suatu tipe, memungkinkan Anda menentukan fungsionalitas apa yang disediakan suatu tipe tanpa mendikte bagaimana ia menyediakannya.
- Mengimplementasikan trait pada suatu tipe mirip dengan mengimplementasikan metode reguler.
- Perbedaannya adalah setelah
impl, kita letakkan nama trait yang ingin kita implementasikan, lalu gunakan kata kuncifor, dan kemudian tentukan nama tipe yang ingin kita implementasikan trait tersebut. - Traits dapat menyediakan implementasi metode default yang bisa di-override oleh tipe.
- Sintaks
impl Traitberfungsi untuk kasus yang sederhana tetapi sebenarnya adalah sintaks gula untuk bentuk yang lebih panjang yang dikenal sebagai trait bound. - Kita juga dapat menentukan lebih dari satu trait bound menggunakan sintaks
+.
Mendefinisikan Trait
-
Sebuah trait mendefinisikan metode yang dapat diimplementasikan oleh tipe lain.
trait Summary { fn summarize(&self) -> String; }
Mengimplementasikan Trait
-
Mengimplementasikan trait mirip dengan mengimplementasikan metode dengan meletakkan nama trait setelah
impldan kemudian menentukan tipe setelahfor.struct NewsArticle { headline: String, location: String, author: String, content: String, } impl Summary for NewsArticle { fn summarize(&self) -> String { format!("{}, by {} ({})", self.headline, self.author, self.location) } } struct Tweet { username: String, content: String, reply: bool, retweet: bool, } impl Summary for Tweet { fn summarize(&self) -> String { format!("{}: {}", self.username, self.content) } }
Default Implementation
-
Traits dapat menyediakan implementasi metode default yang bisa di-override oleh tipe.
trait Summary { fn summarize(&self) -> String { "Default Summary".to_string() } }
Traits sebagai Parameter
-
Traits dapat digunakan sebagai parameter menggunakan sintaks
impl Trait.fn notify(item: &impl Summary) { println!("Breaking news! {}", item.summarize()); } -
Sintaks
impl Traitberfungsi untuk kasus yang sederhana tetapi sebenarnya adalah sintaks gula untuk bentuk yang lebih panjang yang dikenal sebagai trait bound; bentuknya seperti ini:pub fn notify<T: Summary>(item: &T) { println!("Breaking news! {}", item.summarize()); } -
Kita juga bisa memiliki banyak parameter yang mengimplementasikan sebuah trait.
-
Misalnya, fungsi ini memiliki dua parameter yang mengimplementasikan Summary:
pub fn notify(item1: &impl Summary, item2: &impl Summary) { -
Kita juga bisa melakukannya menggunakan generics.
-
Tetapi dalam contoh di bawah, tipe generik
Tyang ditentukan sebagai tipe parameteritem1danitem2membatasi fungsi tersebut sehingga tipe konkret dari nilai yang diberikan sebagai argumen untukitem1danitem2harus sama.pub fn notify<T: Summary>(item1: &T, item2: &T) { -
Kita juga dapat menggunakan sintaks
impl Traitdi posisi return untuk mengembalikan nilai dari suatu tipe yang mengimplementasikan sebuah trait.fn returns_summarizable() -> impl Summary { NewsArticle { headline: "Good News", location: "Indonesia", author: "Someone", content: "Something", } }
Menentukan Multiple Trait Bounds
-
Kita juga dapat menentukan lebih dari satu trait bound menggunakan sintaks
+.pub fn notify(item: &(impl Summary + Display)) { -
Sintaks
+juga valid dengan trait bounds pada tipe generik.pub fn notify<T: Summary + Display>(item: &T) {
Generic Traits
-
Traits juga bisa bersifat generik, memungkinkan mereka beroperasi pada berbagai tipe.
trait Magic<T> { fn add(&self, other: T) -> T; } impl Magic<String> for i32 { fn add(&self, other: String) -> String { format!("{}:{}", self, other) } } fn main() { println!("{}", 71.add("Magic".to_string())); }
Memperluas Trait yang Ada
-
Kita dapat memperluas trait yang ada dengan menggabungkannya atau menambahkan perilaku tambahan.
trait Printable: Display { fn print(&self) { println!("{}", self); } } impl Printable for i32 {} impl Printable for String {} let number: i32 = 71; number.print(); // Output: 71 let text = String::from("FooBar"); text.print(); // Output: FooBar
Referensi
Last updated on