Struct generik dari chapter sebelumnya masih berupa wadah hening: Locker<T> bisa menyimpan apa pun, tetapi belum tahu melakukan apa pun. Perilaku tinggal di blok impl — pertanyaannya, bagaimana menulis impl untuk struct yang tipenya belum diputuskan? Jawabannya memuat satu aturan yang sering menjegal pemula: blok impl harus mendeklarasikan ulang type parameternya sendiri. Chapter ini membahas tiga bentuk sekaligus — impl yang melayani semua instansiasi, impl yang hanya melayani satu instansiasi pilihan, dan method yang membawa type parameter tambahan miliknya sendiri.
impl yang Melayani Semua Instansiasi #
Kita lengkapi Locker<T> dengan dua method dasar: membaca isi dan mengganti isi:
struct Locker<T> {
content: T,
}
impl<T> Locker<T> {
fn reveal(&self) -> &T {
&self.content
}
fn replace(&mut self, new_content: T) {
self.content = new_content;
}
}
fn main() {
let mut code_locker = Locker { content: 4582 };
println!("Kode awal: {}", code_locker.reveal());
code_locker.replace(9103);
println!("Kode baru: {}", code_locker.reveal());
let name_locker = Locker { content: String::from("Kunci cadangan") };
println!("Isi loker nama: {}", name_locker.reveal());
}Output yang Dihasilkan:
Kode awal: 4582
Kode baru: 9103
Isi loker nama: Kunci cadanganMari kita bedah kode di atas baris per baris:
- Baris 5 (
impl<T> Locker<T> {): blokimplmembuka deklarasi<T>-nya sendiri.Tpada baris ini adalah parameter baru yang menggantikan seluruh rentang instansiasi — blok ini berlaku untukLocker<i32>,Locker<String>, dan instansiasi lainnya sekaligus. Penulisanimpl<T>adalah pernyataan “untuk setiapT”. - Baris 6 (
fn reveal(&self) -> &T {): method pembaca dengan penerima&self— pola pinjaman yang sudah kita kuasai. Nilai kembali&Tberarti “reference ke konten bertipe apa pun instansiasi ini”. - Baris 10 (
fn replace(&mut self, new_content: T)): method pengubah. Parameternew_contentbertipeT— tipe konten yang tepat untuk loker ini — dan kepemilikannya berpindah dari pemanggil ke fieldcontentsaat baris 11 menugaskannya. - Baris 17-20 (pemakaian pada
code_locker): instansiasiLocker<i32>;replace(9103)mengirimi32yang cocok denganT = i32. Bindingmutwajib karenareplacememinta&mut self. - Baris 22-23 (pemakaian pada
name_locker): instansiasiLocker<String>memanggil method yang sama — satu blokimplmelayani dua tipe tanpa duplikasi.
Kesalahan paling umum saat pertama kali menulis blok ini adalah lupa mendeklarasikan ulang parameternya:
struct Locker<T> {
content: T,
}
impl Locker<T> {
fn reveal(&self) -> &T {
&self.content
}
}
fn main() {
let locker = Locker { content: String::from("Kunci cadangan") };
println!("{}", locker.reveal());
}Pesan error kompilator:
error[E0425]: cannot find type `T` in this scope
--> main.rs:5:13
|
5 | impl Locker<T> {
| ^ not found in this scope
|
help: you might be missing a type parameter
|
5 | impl<T> Locker<T> {
| +++
error[E0425]: cannot find type `T` in this scope
--> main.rs:6:26
|
6 | fn reveal(&self) -> &T {
| ^ not found in this scope
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0425`.Pesan E0425 menegaskan bahwa T pada blok impl adalah variabel tersendiri: deklarasi <T> pada struct sudah “habis pakai” setelah definisi struct, dan blok impl harus mendeklarasikan parameternya sendiri agar T masuk ke scope. Saran kompilator persis menunjuk perbaikannya — tambahkan impl<T>.
Method yang Menukar Posisi Parameter #
Method pada blok impl<T> tidak hanya memakai parameter struct — method boleh menyusun tipe baru darinya, termasuk menukar posisi. Struct Pair<A, B> berikut mendapat method swap yang membalik urutan kedua field:
struct Pair<A, B> {
first: A,
second: B,
}
impl<A, B> Pair<A, B> {
fn swap(self) -> Pair<B, A> {
Pair { first: self.second, second: self.first }
}
}
fn main() {
let player = Pair { first: String::from("Budi"), second: 7 };
let flipped = player.swap();
println!("Nomor {} dipakai {}", flipped.first, flipped.second);
}Output yang Dihasilkan:
Nomor 7 dipakai BudiMari kita bedah kode di atas baris per baris:
- Baris 7 (
fn swap(self) -> Pair<B, A> {): tipe kembali menukar posisi parameter —Pair<B, A>. Penerimaselftanpa&berarti method ini mengonsumsi instance, mengikuti tiga bentukselfdari Chapter Metode dengan impl; setelahswapdipanggil,playertidak bisa dipakai lagi. - Baris 8 (
Pair { first: self.second, second: self.first }): fieldsecond(bertipeB) pindah ke posisifirst, dan sebaliknya. Kedua nilai berpindah kepemilikan dari instance lama ke instance baru tanpa penyalinan. - Baris 14 (
let flipped = player.swap();): instansiasi masukPair<String, i32>, instansiasi keluarPair<i32, String>— transformasi murni di level tipe, danflipped.firstkini bertipei32sedangkanflipped.secondbertipeString.
Method juga boleh mendeklarasikan type parameter tambahan miliknya sendiri — misalnya fn merge<C>(self, other: Pair<A, B>) -> Pair<A, C> — saat method menerima nilai bertipe yang tidak tercatat pada struct. Pola ini umum pada method koleksi di standard library; kita cukup ingat aturan urutannya: parameter struct dideklarasikan pada impl<A, B>, parameter milik method dideklarasikan pada fn nama<C>(...).
impl untuk Satu Instansiasi Saja #
Kadang sebuah perilaku hanya masuk akal untuk satu instansiasi tertentu. to_uppercase() adalah operasi teks — memberikan method ini pada Locker<i32> jelas tidak bermakna. Rust mengizinkan blok impl dengan tipe konkret:
struct Locker<T> {
content: T,
}
impl<T> Locker<T> {
fn reveal(&self) -> &T {
&self.content
}
}
impl Locker<String> {
fn shout(&self) -> String {
self.content.to_uppercase()
}
}
fn main() {
let note = Locker { content: String::from("rapat pukul 9") };
println!("Bentuk asli: {}", note.reveal());
println!("Bentuk teriak: {}", note.shout());
}Output yang Dihasilkan:
Bentuk asli: rapat pukul 9
Bentuk teriak: RAPAT PUKUL 9Mari kita bedah kode di atas baris per baris:
- Baris 11 (
impl Locker<String> {): blok kedua tidak lagi generik —TdigantikanStringsecara langsung, tanpa deklarasi parameter. Seluruh method di dalamnya hanya dimiliki oleh instansiasiLocker<String>. - Baris 12-14 (
fn shoutdanself.content.to_uppercase()): pemanggilanto_uppercase()hanya mungkin karena di blok iniself.contentpasti bertipeString. Blok generikimpl<T>tidak bisa memanggilnya karenaTpolos tidak menjanjikan method apa pun — pagar yang sama dengan Chapter Fungsi Generik. - Baris 19-22 (pemakaian):
notebertipeLocker<String>sehingga dua method tersedia sekaligus —revealdari blok generik,shoutdari blok khusus.
Pemanggilan method khusus pada instansiasi lain gagal dengan pesan yang sangat jujur:
struct Locker<T> {
content: T,
}
impl<T> Locker<T> {
fn reveal(&self) -> &T {
&self.content
}
}
impl Locker<String> {
fn shout(&self) -> String {
self.content.to_uppercase()
}
}
fn main() {
let note = Locker { content: String::from("rapat pukul 9") };
println!("{}", note.shout());
let code = Locker { content: 4582 };
println!("{}", code.shout());
}Pesan error kompilator:
error[E0599]: no method named `shout` found for struct `Locker<{integer}>` in the current scope
--> main.rs:22:25
|
1 | struct Locker<T> {
| ---------------- method `shout` not found for this struct
...
22 | println!("{}", code.shout());
| ^^^^^ method not found in `Locker<{integer}>`
|
= note: the method was found for `Locker<String>`
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0599`.Pesan E0599 memisahkan dua hal dengan rapi: method shout memang ada di struct Locker, tetapi hanya untuk Locker<String> — baris catatannya menyebut hal itu hampir harfiah. Loker bernomor (Locker<i32>) hanya memiliki method dari blok impl<T>.
Ringkasan #
Blok impl<T> Locker<T> melayani semua instansiasi sekaligus dan wajib mendeklarasikan ulang parameternya — kelalaian ditolak sebagai E0425 lengkap dengan saran perbaikan. Method di dalamnya bebas memakai T pada parameter dan nilai kembali, bahkan menyusun tipe baru seperti swap(self) -> Pair<B, A>, serta boleh membawa type parameter tambahan milik method sendiri. Blok impl Locker<String> menutup spektrum di ujung lain: perilaku eksklusif satu instansiasi, dengan E0599 sebagai penjaga batas. Satu tema masih tersisa dan akan mengikat seluruh section: T polos tidak bisa apa-apa — Chapter Trait Bound memberi kunci pertamanya.