Mengenal Struct, self dan Self pada Rust
Rust merupakan bahasa pemrograman sistem yang tidak berbasis objek namun penggunaan struct
dan trait
konsepnya mirip
pada bahasa pemgrograman oop(berbasis objek).
Rust tidak menggunakan klas untuk mengatur struktur kode, melainkan menggunakan struk untuk mewakili object/klas
dan trait
untuk mewakili behaviornya atau dalam bahasa lain disebut dengan interface
.
Kali ini saya ingin mengenalkan sedikit tentang struct
untuk mengelola struktur kode dan sedikit tentang trait
dan bagaimana
penggunaannya. Karena pembahasan struct dan trait bisa sangat panjang maka disini saya hanya menjelaskan secara umum belum sampai mendalam.
Kemudian kita juga akan mengenal apa itu keyword self
di Rust. Karena jika kamu sudah menggunakan struct dan ingin mengimplementasi
method pada struk Rust tentu akan bertemu dengan keyword self
. Dan juga Rust memiliki keyword Self
(dengan huruf S besar) yang akan
menunjuk ke nama struct yang sedang di impl
.
self
(dengan huruf s kecil) pada rust biasanya digunakan pada saat deklarasi method untuk mengakses anggota struct, ya method kalau
dalam pemrograman berbasis objek adalah fungsi yang menempel pada objek. self
mirip self pada python atau this pada Javascript.
Penggunaan self
pada Rust biasanya pada tipe data struct
/enum
.
Mengenal Struct
Struct atau structure adalah tipe data custom yang membolehkanmu untuk memberinya nama dan mempaketkan nilai yang saling berhubungan
dan membuatnya memiliki arti. Jika kamu terbiasa dengan bahasa pemrograman OOP struct
itu mirip class
Mendefinisikan dan instantiating struct
Karena arti instantiating
bahasa Indonesia menurut saya agak aneh maka saya gunakan bahasa inggris saja. Karena seharusnya
instantiating kalau dalam OOP berarti new NamaKlass
. Ok kita balik lagi ke struk, pertama kita akan belajar cara mendefinisikan struct.
Untuk mendefinisikan struct dapat digunakan keyword struct
diikuti nama struk kemudian didalam kurung kurawal kita definisikan nama
dan tipe data anggota struk atau biasa dikenal dengan istilah fields
.
Untuk lebih memahami struk mari kita lihat contoh berikut ini:
struct User {
id: i32,
username: String,
email: String,
active: bool,
}
Jadi disini kita membuat struk dengan nama User
dan memiliki 4 fields yaitu id, username, email dan active
. Masing-masing memiliki tipe data.
Untuk menggunakannya kita perlu membuat instance dari struk dengan memberikan nilai-nilai pada field yang ada pada struk. Untuk contohnya bisa dilihat pada kode berikut ini:
fn main() {
let user = User{
id: 1,
username: String::from("agus"),
email: String::from("agus@gmail.com"),
active: true,
};
}
Untuk mengakses field pada struk bisa menggunakan notasi dot (.)
. Jika kita hanya ingin mendapatkan email saja maka kita bisa
gunakan user.email
dimanapun kita ingin menginginkan nilai ini. Jika instance struct mutable(dapat diubah) kita bisa merubah nilai dari field tersebut menggunakan notasi dot.
Kode berikut menunjukkan bagaimana cara mengganti nilai pada field email
dari instansi User
.
fn main() {
let mut user = User{
id: 1,
username: String::from("agus"),
email: String::from("agus@gmail.com"),
active: true,
};
user.email = "hello@gmail.com";
}
Perlu dicatat bahwa seluruh instance harus mutable; Rust tidak membolehkan kita untuk menandai hanya field tertentu saja yang dapat diubah.
Mengenal impl
dan self
Untuk mendefinisikan method pada struk kita bisa menggunakan keyword impl
diikuti dengan nama struk yang ingin di implementasi method.
Contoh berikut ini akan mengimpl method print
pada struk User
.
impl Struct {
fn print(&self) {
println!("ID: {}, Username: {}, Email: {}, Active: {}",
self.id, self.username, self.email, self.active);
}
}
fn main() {
let user = User{
id: 1,
username: String::from("agus"),
email: String::from("agus@gmail.com"),
active: true,
};
user.print();
}
Jika kode diatas dijalankan maka memberikan output seperti ini:
Disini self
mirip this
pada javascript yang akan bisa mengakses field pada struct karena impl User
. Kalau kode diatas ditulis dengan javascript akan seperti ini
class User {
id = 0;
username;
email:
active = false;
print() {
console.log(`ID: ${this.id}, Username: ${this.username},
Email: ${this.email}, Active: ${this.active}`)
}
}
Jika kita mendefinisikan method pada struct dengan impl
namun pada parameter fungsi tidak ada self/&self/&mut self
maka fungsi tersebut akan menjadi static.
Kita tidak perlu menginstansi struct sebelum menggunakan fungsi tersebut. Biasanya digunakan untuk membuat fungsi instatnsi struck/ model builder begitu.
Kalau dalam javascript mirip fungsi static pada class, untuk lebih jelasnya bisa diliat pada kode berikut:
class User {
static add(x, y) {
const result = x + y
console.log(`${x} + ${y} = ${result}`)
}
}
User.add(1, 2)
Bisa dilihat pada kode di atas bahwa untuk mengakses fungsi add
kita langsung memanggil nama klass dan nama fungsinya tanpa harus instansi terlebih dulu.
Nah pada Rust kurang lebih mirip dengan javascript dengan perbedaan bahwa kita tidak bisa mengakses field struck.
impl User {
fn new(id: i32, username: String, email: String, active: bool) -> User {
User {
id,
username,
email,
active,
}
}
}
Untuk memanggil fungsi static maka kita bisa gunakan tanda ::
setelah nama struck. contoh:
let user = User::new(1, "agus".to_owned(), "agus@gmail.com".to_owned(), true);
Pada definisi fungsi new
di atas terdapat tipe kembalian berupa struct User
, agar lebih idiom ke Rust maka kita bisa
menggantinya dengan Self
, Self
akan mengembalikan struct sesuai dengan yang di impl
. seperti pada contoh code di atas
jika Self
didalam impl User
maka akan mengembalikan struct User
. Keuntungan menggunakan Self
dari pada nama struct adalah
jika struct berubah kita tidak perlu merubah tipe kembaliannya.
Contoh kode yang sudah diganti dengan Self
:
impl User {
fn new(id: i32, username: String, email: String, active: bool) -> Self {
Self {
id,
username,
email,
active,
}
}
}
Mengenal Trait
Trait adalah kumpulan definisi fungsi yang belum diimplementasi, trait dapat berisi 1 atau lebih definisi fungsi. Namun trait juga dapat berisi implementasi default dari suatu fungsi yang akan dipakai jika struct/enum/type tidak mengimplementasi definisi fungsi pada trait.
Contoh definisi trait pada trait std::io::Read
pub trait Read {
fn read(&mut self, buf: &mut [u8]) -> Result<usize>;
}
Pada kode diatas saya mengambil contoh satu fungsi read
pada trait Read
dimana parameternya berupa &mut [u8]
yang artinya kita harus menyediakan
variabel mutable dengan tipe slice u8. Semua tipe yang mengimplementasi trait Read
kita dapat mengakses fungsi read
tersebut. Kita ambil contoh dari struct File pada module
std::fs::File
.
use std::io::prelude::*;
use std::io;
use std::fs::File;
fn main() -> io::Result<()> {
let mut f = File::open("foo.txt")?;
let mut buffer = [0; 10];
let n = f.read(&mut buffer)?;
println!("Hasil: {:?}", &buffer[..n]);
Ok(())
}
Karena struct File
mengimplementasi trait Read
maka kita bisa menggunakan fungsi read
untuk membaca file. Pada kode di atas pertama
kita membuka file pada kode let mut f
, kemudian kita membuat buffer file let mut buffer
dengan nilai 0 sebanyak 10. Lalu pada let n
kita baca file f.read(&mut buffer)?
dan taruh pada
variabel buffer
.
Kemudian kita mencetak datanya sesuai dengan hasil pembacaan dari varibel n
berapa banyak yang sudah dibaca &buffer[..n]
.
Contoh lain penggunaan trait
struct News;
struct Blog;
trait Summary {
fn read_more(&self) -> String;
}
impl Summary for News {
fn read_more(&self) -> String {
"Baca berita lagi..".to_owned()
}
}
impl Summary for Blog {
fn read_more(&self) -> String {
"Baca blog lagi..".to_owned()
}
}
fn print_summary(sum: impl Summary) -> {
println!("{}", sum.read_more());
}
fn main() {
let news = News;
let blog = Blog;
print_summary(news);
print_summary(blog);
}
Dapat dilihat pada kode di atas bahwa kita mendefinisikan 2 buah struk News
dan Blog
, dan juga trait Summary
. Lalu mengimplementasi
trait Summary
untuk struk News
dan Blog
dengan default implementasi pada fungsi read_more
. Kita juga membuat fungsi print_summary
dengan parameter sum
dengan aturan bahwa sum
harus implement dari trait Summary
. Selama sum
mengimplementasi dari trait Summary
maka
kode akan berjalan.
Untuk lebih memahami trait kita bisa mengubah kode di atas ke dalam bahasa PHP. kurang lebih kodenya seperti ini:
interface Summary {
public function read_more(): string;
}
class News implements Summary
{
public function read_more(): string
{
return 'Baca berita lagi..';
}
}
class Blog implements Summary
{
public function read_more(): string
{
return 'Baca blog lagi..';
}
}
function print_summary(Summary $sum) {
echo $sum->read_more(), PHP_EOL;
}
$news = new News();
$blog = new Blog();
print_summary($news);
print_summary($blog);
Karena di PHP tidak ada struk maka disini saya pake class. Semoga bermanfaat.