Generic Types & Traits

Generic Types & Traits

Generic adalah cara untuk membuat tipe data menjadi lebih flexible, sehingga mudah untuk dipakai, berulang-ulang dan terhindar dari masalah duplicate.

Menghapus duplikasi dengan mengekstraknya menjadi sebuah function

Sebelum jauh membahas tentang generic type, ada baiknya untuk melihat sebuah studi kasus sederhana yaitu mengurangi duplication dengan merubahnya menjadi function yang dapat dipakai berulang-ulang.

Kita akan coba membuat sebuah program sederhana untuk mencari angka terbesar dari sebuah koleksi data number, berikut adalah contoh kodenya.

fn main() {
let numbers = vec![1, 2, 3, 4, 5];
let mut largest = numbers[0];
for number in numbers {
if number > largest {
largest = number
}
}
println!("{}", largest); // 5
}

Variable numbers di atas adalah tipe data vector yang menampung daftar angka dari 1 sampai 5, kemudian setelahnya ada mutable variable yang akan mengambil isi index pertama dari variable numbers, kemudian kita coba mengambil semua item yang ada di variable numbers dengan menggunakan loop dan didalam perulangan tersebut kita menggunakan if statement untuk mengecek item number, jika lebih besar dari mutable variable largest maka akan dimasukan sebagai nilai baru di variable largest.

Pertanyaannya bagaimana jika kita ingin menggunakan fungsi yang sama di tempat yang berbeda, tentunya hal yang paling mudah adalah mengekstrak fungsi tadi kedalam sebuah fungsi.

fn largest(list: &[i32]) -> i32 {
let mut largest = list[0];
for &number in list {
if number > largest {
largest = number
}
}
largest
}
fn main() {
let numbers = vec![1, 2, 3, 4, 5];
let result = largest(&numbers);
println!("{}", result);
}

Setelah refactor dengan mengekstrak sebuah operasi pencarian angka terbesar didalam sebuah koleksi data number, sekarang fungsi largest menjadi sangat flexible dan dapat dipakai berkali - kali tanpa perlu menulis ulang kodenya.

Fungsi largest memiliki sebuah parameter yang mana merepresentasikan nilai konkret dari slice i32 kedalam sebuah function.

Pertanyaan yang terjadi selanjutnya adalah bagaimana jika kita ingin menggunakan fungsi tersebut untuk tipe data yang berbeda, nah untuk menjawab hal tersebut perlu untuk memahami generic types.

Generic Data Types

Masih terkait studi kasus sebelumnya dimana ada sebuah function yang bisa mengevaluasi sebuah integer dan kemudian mengembalikan angka yang paling besar yang terdapat pada sebuah koleksi data.

Misalnya fungsi tadi kita ingin bisa digunakan untuk tipe data lainnya, yaitu untuk evaluasi sebuah tipe data string.

Dengan menggunakan generic type hal tersebut memungkinkan, untuk menggunakan tipe data generic didalam sebuah function cukup dengan merubah fungsi sebelumnya menjadi seperti ini fn largest<T>(list: &[T]) -> T {} sehingga pada penggunaannya dapat disesuaikan tipe datanya.

fn largest<T>(list: &[T]) -> T {
let mut largest = list[0];
for &item in list {
if item > largest {
largest = item
}
}
largest
}
fn main() {
let numbers = vec![10, 1, 2];
let largest_number = largest(&numbers);
println!("{}", largest_number);
let words = vec!["hello", "adiatma"];
let largest_words = largest(&words);
println("{}", largest_words);
}
Compiling rust-by-example v0.1.0 (/Users/adiatma/Work/rust-by-example)
error[E0423]: expected function, found macro `println`
--> src/main.rs:20:5
|
20 | println("{}", largest_words);
| ^^^^^^^ help: use `!` to invoke the macro: `println!`
error[E0369]: binary operation `>` cannot be applied to type `T`
--> src/main.rs:5:17
|
5 | if item > largest {
| ---- ^ ------- T
| |
| T
|
= note: `T` might need a bound for `std::cmp::PartialOrd`
error: aborting due to 2 previous errors
Some errors have detailed explanations: E0369, E0423.
For more information about an error, try `rustc --explain E0369`.
error: could not compile `rust-by-example`

Loh, kok error?

Pada capture error diatas yang di mention adalah std::cmp::PartialOrd yang bisa disebut trait.

Saat ini error state yang terjadi diatas adalah karena fungsi largest tidak bekerja untuk setiap kemungkinan T type, karena kita ingin membandingkan tipe data T yang terdapat didalam function. Untuk perbandingan kita perlu untuk menggunakan std::cmp::PartialOrd.

Generic Type in Struct

Tipe umum atau generic type bisa juga digunakan di struct, gambarnya seperti contoh dibawah.

struct Point<T> {
x: T,
y: T
}
fn main() {
let integer = Point { x: 1, y: 2 };
let float = Point { x: 1.2, y: 2.1 };
println!("{} {}", integer.x, float.y); // 1 2.1
}

In enum definition

Selain bisa digunakan di struct, tipe data generic juga bisa digunakan di enum.

enum Result<T, E> {
Ok(T),
Err(E),
}

In Method Definition

struct Point<T> {
x: T,
y: T
}
impl<T> Point<T> {
fn print_x(&self) -> &T {
&self.x
}
}
fn main() {
let integer = Point { x: 1, y: 2 };
let float = Point { x: 1.2, y: 2.1 };
println!("{} {} {}", integer.x, float.y, integer.print_x()); // 1 2.1 1
}

Multi Generic Types with Method

struct Point<T, U> {
x: T,
y: U
}
impl<T, U> Point<T, U> {
fn mixup<V, W>(self, other: Point<V, W>) -> Point<T, W> {
Point {
x: self.x,
y: other.y
}
}
}
fn main() {
let p1 = Point { x: 1, y: 2 };
let p2 = Point { x: "Hello", y: "h" };
let p3 = p1.mixup(p2);
println!("{} {}", p3.x, p3.y); // 1 h
}

Traits

Sebuah trait dapat meminta kepada kompilator Rust untuk bagian dari functionality type dapat di share dengan tipe data lainnya. Kita bisa menggunakan trait untuk share behaviour dengan cara yang abstrak. Juga kita bisa menggunakan trait untuk mengikat spesifikasi dari _generic type _yang bisa untuk setiap tipe data.

Trait mirip seperti sebuah interface pada bahasa pemrograman lainnya. Seperti sebuah interface pada sebuah object, sebuah trait dapat dibagikan ke beberapa method sehingga punya referensi tipe yang sama. Berikut adalah contoh penggunaan trait pada sebuah method.

trait Summary {
fn summarize(&self) -> String;
}
struct Article {
title: String,
author: String
}
impl Summary for Article {
fn summarize(&self) -> String {
format!("{} author of {}", self.author, self.title)
}
}
fn main() {
let first_article = Article {
title: String::from("Buku Rust Bahasa Indonesia"),
author: String::from("Adiatma")
};
println!("{}", first_article.summarize()); // Adiatma author of Buku Rust Bahasa Indonesia
}