Skip to content

Commit e401484

Browse files
committed
feat: add local trait & orphan rule explanation
1 parent 34f9549 commit e401484

1 file changed

Lines changed: 94 additions & 8 deletions

File tree

docs/basic/traits.md

Lines changed: 94 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,99 @@ Berdasarkan tempat dimana traits dibuat, ada 2 jenis traits:
3333

3434
- **External traits** (atau foreign traits).
3535

36-
Yaitu traits yang tempat dideklarasikannya berada di luar create kode yang ditulis. Misalnya, trait `std::fmt::Debug` dan `std::ops::Add`, keduanya merupakan external traits yang berada di crate `std` atau crate Rust Standard Library.
36+
Yaitu traits yang tempat dideklarasikannya berada di luar crate kode yang ditulis. Misalnya, trait `std::fmt::Debug` dan `std::ops::Add`, keduanya merupakan external traits yang berada di crate `std` atau crate Rust Standard Library.
3737

38-
Pada case yang berhubungan dengan external traits, kita programmer umumnya hanya fokus ke implementasi saya, bagaimana cara memanfaatkan dan menggunakan external traits yang sudah ada ke dalam kode yang ditulis.
38+
Pada kasus seperti ini, kita biasanya hanya fokus ke cara memakai external traits yang sudah ada.
3939

4040
- **Local traits**.
4141

4242
Adalah traits yang kita ciptakan di crate yang berada di dalam package/project yang sedang kita kerjakan.
4343

44-
Chapter ini fokusnya adalah pembahasan tentang dasar implementasi **external traits** dan cara kerjanya.
44+
Chapter ini fokusnya adalah pembahasan tentang dasar implementasi **external traits** dan cara kerjanya. Setelah itu, kita juga akan lihat sedikit contoh `local trait` supaya bedanya lebih terasa.
45+
46+
### ◉ Aturan penting dalam implementasi trait
47+
48+
Di Rust ada aturan yang perlu diingat:
49+
50+
- `external trait` boleh diimplementasikan ke `local type`
51+
- `local trait` boleh diimplementasikan ke tipe apa pun
52+
- `external trait` tidak boleh diimplementasikan ke `external type`
53+
54+
Aturan ini biasanya disebut *orphan rule* atau *coherence rule*.
55+
56+
Kalau dibahas dengan bahasa sederhana, Rust ingin mencegah dua crate berbeda saling berebut implementasi untuk trait dan type yang sama. Dengan begitu, perilaku program tetap jelas dan tidak ambigu.
57+
58+
Kalau kita tetap ingin memakai `external trait` pada `external type`, solusinya adalah memakai *wrapper pattern*. Caranya, kita bungkus type external tersebut ke type local buatan kita sendiri, lalu implementasikan trait ke wrapper itu.
59+
60+
Contoh sederhananya:
61+
62+
```rust
63+
struct Wrapper(Vec<String>);
64+
65+
impl std::fmt::Display for Wrapper {
66+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
67+
write!(f, "{:?}", self.0)
68+
}
69+
}
70+
```
71+
72+
Pada contoh di atas:
73+
74+
- `Vec<String>` adalah `external type`
75+
- `Display` adalah `external trait`
76+
- `Wrapper` adalah `local type`
77+
78+
Karena `Wrapper` adalah type lokal, kita bebas mengimplementasikan trait ke sana.
79+
80+
Pemakaiannya di `main` bisa seperti ini:
81+
82+
```rust
83+
fn main() {
84+
let v = Wrapper(vec![String::from("a"), String::from("b")]);
85+
println!("{}", v);
86+
}
87+
```
88+
89+
Output:
90+
91+
> ["a", "b"]
92+
93+
### ◉ Contoh local trait
94+
95+
Sekarang kita coba kebalikannya: membuat trait sendiri, lalu mengimplementasikannya ke type yang sudah ada.
96+
97+
Misalnya kita punya trait sederhana bernama `Message`:
98+
99+
```rust
100+
trait Message {
101+
fn log(&self);
102+
}
103+
```
104+
105+
Lalu kita implementasikan ke `String`:
106+
107+
```rust
108+
impl Message for String {
109+
fn log(&self) {
110+
println!("{}", self);
111+
}
112+
}
113+
```
114+
115+
Karena `Message` adalah `local trait`, kita boleh mengimplementasikannya ke type `String` yang berasal dari standard library.
116+
117+
Pemakaiannya di `main` akan terlihat seperti ini:
118+
119+
```rust
120+
fn main() {
121+
let s = String::from("hello");
122+
s.log();
123+
}
124+
```
125+
126+
Output:
127+
128+
> hello
45129
46130
## A.36.2. Implementasi trait
47131

@@ -82,7 +166,7 @@ struct Circle {
82166

83167
Hasilnya error, karena struct `Circle` yang dibuat tidak implement trait `std::fmt::Debug`.
84168

85-
Solusi agar tidak error adalah dengan mengimplementasikan trait `std::fmt::Debug` ke tipe `Circle`, dengan itu semua data bertipe `Circle` akan bisa di-print menggunakan formatted print `{:?}`.
169+
Solusi agar tidak error adalah dengan mengimplementasikan trait `std::fmt::Debug` ke tipe `Circle`, dengan begitu semua data bertipe `Circle` akan bisa di-print menggunakan formatted print `{:?}`.
86170

87171
> Selain via implementasi trait, tipe data custom bisa di-print dengan cara menambahkan atribut `#[derive(Debug)]` pada definisi tipe data-nya. Namun kita tidak membahas itu pada chapter ini.
88172
@@ -100,7 +184,7 @@ pub trait Debug {
100184

101185
Trait `Debug` mempunyai satu spesifikasi method, bernama `fmt` yang detail strukturnya bisa dilihat di atas.
102186

103-
Kita akan implement trait `Debug` ini ke tipe `Circle`, maka wajib hukumnya untuk menuliskan implementasi method sesuai dengan yang ada di trait `Debug`.
187+
Kita akan implement trait `Debug` ini ke tipe `Circle`, maka wajib untuk menuliskan implementasi method sesuai dengan yang ada di trait `Debug`.
104188

105189
Di bawah ini adalah contoh cara implementasi trait.
106190

@@ -157,7 +241,7 @@ impl std::fmt::Debug for Circle {
157241
}
158242
```
159243

160-
Kemudian tulis implementasi method `fmt` dalam block method. Tulis statement macro `write` untuk data string (yang ingin di-print) dengan tujuan adalah variabel `f`.
244+
Kemudian tulis implementasi method `fmt` dalam block method. Di sini kita memakai macro `write` untuk menulis data string yang ingin di-print ke variabel `f`.
161245

162246
Di contoh, format `Circle radius: {}` digunakan. Dengan ini nantinya saat printing data, yang muncul adalah text `Circle radius: {}`.
163247

@@ -169,7 +253,7 @@ impl std::fmt::Debug for Circle {
169253
}
170254
```
171255

172-
> Tips untuk pengguna visual studio code dengan rust-analyzer extension ter-install, setelah selesai menulis block kode `impl`, cukup jalankan `ctrl+space` atau `cmd+space` untuk men-trigger autocomplete suggestion. Kemudian klik opsi method yang ada di situ, maka kode implementasi method langsung muncul dengan sendirinya.
256+
> Tips untuk pengguna visual studio code dengan rust-analyzer extension ter-install: setelah selesai menulis block kode `impl`, cukup jalankan `ctrl+space` atau `cmd+space` untuk men-trigger autocomplete suggestion. Kemudian klik opsi method yang ada di situ, maka kode implementasi method langsung muncul dengan sendirinya.
173257
174258
### ◉ Macro `write`
175259

@@ -195,7 +279,7 @@ Coba tambahkan statement `println`, tetapi kali ini gunakan formatted print `{}`
195279

196280
![Trait](img/traits-4.png)
197281

198-
Hasilnya error, karena trait `std::fmt::Debug` hanya berguna untuk formatted print `{:?}`. Agar data bertipe `Circle` bisa di-print menggunakan formatted print `{}` maka trait `std::fmt::Display` harus diimplementasikan juga.
282+
Hasilnya error, karena trait `std::fmt::Debug` hanya berguna untuk formatted print `{:?}`. Kalau ingin `Circle` bisa di-print dengan `{}`, maka trait `std::fmt::Display` juga harus diimplementasikan.
199283

200284
Ubah kode dengan menambahkan implementasi trait `Display`. Hasilnya kurang lebih seperti ini:
201285

@@ -220,6 +304,8 @@ impl std::fmt::Display for Circle {
220304
> - Link dokumentasi trait `Debug` https://doc.rust-lang.org/std/fmt/trait.Debug.html
221305
> - Link dokumentasi trait `Display` https://doc.rust-lang.org/std/fmt/trait.Display.html
222306
307+
> Lebih detail tentang trait bound, wrapper pattern, dan aturan implementasi trait untuk type lokal maupun external ada di chapter [Traits ➜ Advanced](/basic/advanced-traits).
308+
223309
---
224310

225311
## Catatan chapter 📑

0 commit comments

Comments
 (0)