Smart Pointer Rust Dijelaskan: Box, Rc, Arc, dan RefCell di 2026
Smart pointer Rust Box, Rc, Arc, dan RefCell dijelaskan dengan contoh 2026 yang dapat dikompilasi, tabel keputusan, dan pertanyaan wawancara yang umum.

Smart pointer di Rust adalah perkakas yang membuka pola kepemilikan yang tidak bisa diungkapkan oleh borrow checker sendirian: alokasi heap, kepemilikan bersama, dan mutasi di balik sebuah referensi bersama. Ketika sebuah referensi biasa (&T) hanya meminjam sebuah nilai, sebuah smart pointer memiliki datanya dan menambahkan perilaku ekstra di atasnya. Panduan ini menguraikan empat tipe yang ditemui setiap pengembang Rust di produksi maupun dalam wawancara: Box, Rc, Arc, dan RefCell, dengan contoh yang dapat dikompilasi menargetkan Rust edisi 2024.
Gunakan Box<T> untuk alokasi heap dengan pemilik tunggal, Rc<T> untuk kepemilikan bersama pada satu thread, Arc<T> untuk kepemilikan bersama antar-thread, dan RefCell<T> untuk memutasi sebuah nilai melalui referensi bersama. Pasangan Rc<RefCell<T>> dan Arc<Mutex<T>> menangani keadaan bersama yang dapat diubah.
Apa itu smart pointer di Rust
Sebuah smart pointer adalah struct yang berperilaku seperti pointer tetapi membawa metadata atau kemampuan ekstra. Sebagian besar mengimplementasikan trait Deref, sehingga *pointer dan pemanggilan metode bekerja seolah-olah pointer tersebut adalah referensi biasa, serta trait Drop, sehingga pembersihan berjalan otomatis ketika nilai keluar dari scope. Pustaka standar menyediakan keempat tipe yang dibahas di sini, dan memahaminya bergantung pada penguasaan yang kuat atas kepemilikan dan peminjaman, yang menentukan siapa yang membebaskan setiap alokasi dan kapan.
Perbedaan utama dari sebuah referensi adalah kepemilikan. &T tidak pernah memiliki data yang ditunjuknya, sehingga ia tidak bisa hidup lebih lama daripada nilainya. Sebuah smart pointer memiliki datanya, mengendalikan masa hidupnya, dan membebaskannya secara deterministik. Bab Rust Book tentang smart pointer memperlakukannya sebagai satu kategori justru karena mereka berbagi bentuk kepemilikan-plus-perilaku ini.
Box<T>: alokasi heap untuk tipe rekursif dan bertipe pasti
Box<T> adalah smart pointer yang paling sederhana. Ia menyimpan sebuah nilai di heap dan menyimpan pointer ke nilai itu di stack, dengan satu pemilik dan tanpa overhead runtime selain alokasi itu sendiri. Tugasnya yang paling umum adalah memberi tipe rekursif ukuran yang diketahui: sebuah tipe yang memuat dirinya sendiri secara langsung akan menjadi tak terhingga besar, tetapi sebuah Box hanyalah sebuah pointer, sehingga ukurannya tetap terlepas dari apa yang ditunjuknya.
Contoh di bawah mendefinisikan sebuah pohon biner. Setiap Node memuat dua anak, dan tanpa indireksi kompiler tidak dapat menghitung ukuran Tree. Membungkus setiap anak dalam sebuah Box memutus rekursi di tingkat tipe.
#[derive(Debug)]
enum Tree {
Leaf(i32),
// Box<Tree> puts each child on the heap, so Node has a fixed size (two pointers).
Node(Box<Tree>, Box<Tree>),
}
fn sum(tree: &Tree) -> i32 {
match tree {
Tree::Leaf(value) => *value, // base case: return the leaf
Tree::Node(left, right) => sum(left) + sum(right), // recurse into both children
}
}
fn main() {
// Build (1) + ((2) + (3)) = 6
let tree = Tree::Node(
Box::new(Tree::Leaf(1)),
Box::new(Tree::Node(
Box::new(Tree::Leaf(2)),
Box::new(Tree::Leaf(3)),
)),
);
println!("sum = {}", sum(&tree)); // sum = 6
}Box juga penting ketika memindahkan sebuah nilai besar akan mahal, atau ketika mengembalikan sebuah trait object seperti Box<dyn Error>. Dalam setiap kasus aturannya sama: satu pemilik, dibebaskan otomatis ketika Box di-drop.
Rc<T>: kepemilikan bersama pada kode single-thread
Terkadang sebuah nilai membutuhkan beberapa pemilik, dan tak satu pun di antaranya jelas menjadi yang terakhir menggunakannya. Rc<T> (reference counted) menyelesaikan ini dengan menyimpan hitungan kuat berapa banyak pemilik yang ada. Rc::clone menaikkan hitungan itu tanpa menyalin data yang mendasarinya, dan setiap drop menurunkannya. Ketika hitungan mencapai nol, nilainya dibebaskan.
Bayangkan sebuah objek konfigurasi yang dibaca oleh beberapa worker. Setiap worker harus memegang konfigurasi selama masih dibutuhkan, dan alokasinya baru boleh hilang ketika worker terakhir sudah tiada.
use std::rc::Rc;
#[derive(Debug)]
struct Config {
endpoint: String,
timeout_ms: u32,
}
fn main() {
// Rc::new moves Config onto the heap with a strong count of 1.
let config = Rc::new(Config {
endpoint: "https://api.example.com".to_string(),
timeout_ms: 5000,
});
// Rc::clone only bumps the reference count; it does not deep-copy Config.
let worker_a = Rc::clone(&config);
let worker_b = Rc::clone(&config);
// All three handles point to the same allocation.
println!("endpoint: {}", worker_a.endpoint);
println!("timeout: {}", worker_b.timeout_ms);
// strong_count reports how many owners currently hold the value.
println!("owners: {}", Rc::strong_count(&config)); // owners: 3
}Memanggil Rc::clone secara eksplisit (alih-alih config.clone()) adalah idiomatik: ia memberi sinyal kepada pembaca bahwa operasi ini murah dan hanya menyentuh sebuah penghitung. Tangkapannya adalah Rc<T> hanya memberikan referensi bersama yang immutable. Ia tidak dapat memutasi nilai yang dibagikannya, dan tidak aman dikirim antar-thread. Dokumentasi Rc menjelaskan kedua batasan tersebut.
RefCell<T> dan mutabilitas interior di Rust
Rust secara normal menegakkan bahwa sebuah nilai entah dibagikan melalui banyak referensi immutable atau dimutasi melalui tepat satu referensi mutable, dan ia memeriksanya pada waktu kompilasi. RefCell<T> menyediakan mutabilitas interior: ia memungkinkan kode memutasi sebuah nilai melalui referensi bersama dengan memindahkan pemeriksaan yang sama itu ke waktu runtime. borrow() mengembalikan sebuah guard baca bersama; borrow_mut() mengembalikan sebuah guard tulis eksklusif. Aturannya identik, tetapi pelanggaran memicu panic alih-alih gagal dikompilasi.
Digabung dengan Rc, ini menghasilkan pola bersama-mutable single-thread yang kanonik, Rc<RefCell<T>>: beberapa pemilik yang semuanya dapat memperbarui nilai yang sama.
use std::rc::Rc;
use std::cell::RefCell;
// Rc gives shared ownership; RefCell allows mutation through a shared reference.
type SharedCounter = Rc<RefCell<u32>>;
fn increment(counter: &SharedCounter) {
// borrow_mut() hands out an exclusive reference, checked at runtime.
*counter.borrow_mut() += 1;
}
fn main() {
let counter: SharedCounter = Rc::new(RefCell::new(0));
let handle_a = Rc::clone(&counter);
let handle_b = Rc::clone(&counter);
increment(&handle_a);
increment(&handle_b);
increment(&counter);
// borrow() gives a shared read guard; the value is now 3.
println!("count = {}", counter.borrow()); // count = 3
}Memanggil borrow_mut() selagi sebuah guard borrow() atau borrow_mut() lain masih hidup akan memicu panic dengan already borrowed: BorrowMutError. Jaminan keamanan tetap berlaku, tetapi bug logika yang seharusnya ditangkap kompiler berubah menjadi crash pada waktu runtime. Jaga agar guard berumur pendek dan hindari menahan sebuah borrow melintasi pemanggilan fungsi yang mungkin masuk kembali ke RefCell yang sama.
Mutabilitas interior juga tempat siklus referensi bersembunyi. Dua nilai Rc<RefCell<T>> yang saling menunjuk membuat hitungan kuat satu sama lain tetap di atas nol selamanya, sehingga tak satu pun pernah dibebaskan. Solusinya adalah Weak<T>, sebuah handle non-pemilik yang tidak memengaruhi hitungan kuat; penjelasan klasiknya ada di Learn Rust With Entirely Too Many Linked Lists.
Arc<T>: penghitungan referensi yang aman-thread untuk konkurensi
Arc<T> (atomically reference counted) adalah saudara multi-thread dari Rc<T>. API publiknya nyaris identik, tetapi penghitungnya menggunakan operasi atomik, sehingga mengkloning dan men-drop sebuah Arc dari beberapa thread sekaligus tetap benar. Keatomikan itulah alasan Arc dipilih hanya ketika berbagi melintasi batas thread: kenaikan atomik terukur lebih lambat daripada kenaikan biasa yang digunakan Rc.
Karena Arc<T> tetap memberikan referensi immutable, mutasi antar-thread membutuhkan sebuah primitif sinkronisasi. Mutex<T> adalah pasangan yang lazim, memberikan pola Arc<Mutex<T>> yang mencerminkan Rc<RefCell<T>> untuk kode konkuren. Contoh berikut memunculkan empat thread yang masing-masing menaikkan sebuah total bersama seribu kali.
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
// Arc is safe to share across threads; Mutex serializes access to the inner value.
let total = Arc::new(Mutex::new(0u64));
let mut handles = Vec::new();
for _ in 0..4 {
// Each thread gets its own Arc handle (an atomic count bump).
let total = Arc::clone(&total);
handles.push(thread::spawn(move || {
for _ in 0..1000 {
// lock() blocks until the mutex is free, then returns a guard.
let mut value = total.lock().unwrap();
*value += 1;
}
}));
}
for handle in handles {
handle.join().unwrap(); // wait for every thread before reading
}
println!("total = {}", *total.lock().unwrap()); // total = 4000
}Kompiler menegakkan batas itu secara otomatis: Rc<T> bukan Send, sehingga mencoba memindahkannya ke dalam thread::spawn gagal dikompilasi, mendorong penggunaan Arc. Menggunakan Arc di dalam satu thread hanya membayar operasi atomik yang tak pernah dipakai. Dokumentasi Arc membahas jaminan pengurutan memori secara rinci, dan Arc<Mutex<T>> menopang sebagian besar keadaan bersama dalam Rust async dengan Tokio.
Siap menguasai wawancara Rust Anda?
Berlatih dengan simulator interaktif, flashcards, dan tes teknis kami.
Box vs Rc vs Arc vs RefCell: kapan menggunakan masing-masing
Keempat tipe tersusun di sepanjang dua sumbu: berapa banyak pemilik yang dimiliki sebuah nilai, dan apakah ia dapat dimutasi melalui sebuah handle bersama. Tabel berikut merangkum trade-off-nya.
| Tipe | Kepemilikan | Mutasi via handle bersama | Aman-thread | Overhead |
|------|-----------|----------------------------|-------------|----------|
| Box<T> | Tunggal | Tidak | Ya, jika T: Send | Hanya alokasi heap |
| Rc<T> | Bersama | Tidak | Tidak | Hitungan non-atomik |
| Arc<T> | Bersama | Tidak | Ya | Hitungan atomik |
| RefCell<T> | Tunggal | Ya (dicek saat runtime) | Tidak | Flag peminjaman runtime |
Pohon keputusan praktisnya singkat. Butuh satu pemilik di heap: Box. Butuh banyak pemilik pada satu thread: Rc. Butuh banyak pemilik antar-thread: Arc. Butuh memutasi sebuah nilai bersama: bungkus tipe dalamnya dengan RefCell (single-thread) atau Mutex (multi-thread). Panduan Effective Rust tentang tipe referensi dan pointer mencapai penataan yang sama. Mulailah dengan opsi paling tidak berkuasa dan tambahkan kemampuan hanya ketika kompiler memaksanya.
Pertanyaan wawancara smart pointer Rust
Smart pointer adalah topik wawancara yang andal karena menguji apakah seorang kandidat memahami kepemilikan alih-alih sekadar sintaks. Pertanyaan-pertanyaan di bawah sering muncul, dan lebih banyak lagi dikumpulkan dalam modul wawancara smart pointer Rust.
Apa perbedaan antara Rc dan Arc? Keduanya menyediakan kepemilikan bersama melalui penghitungan referensi. Rc menggunakan penghitung non-atomik dan terbatas pada satu thread; Arc menggunakan operasi atomik dan dapat dibagikan antar-thread dengan biaya runtime kecil. Kompiler menegakkan pemisahan itu: Rc bukan Send maupun Sync, sehingga tidak dapat melintasi batas thread.
Mengapa menggabungkan Rc dengan RefCell? Rc memberikan kepemilikan bersama tetapi hanya akses immutable. RefCell menambahkan mutabilitas interior, memungkinkan pemilik memutasi nilai melalui referensi bersama dengan pemeriksaan peminjaman runtime. Bersama-sama, Rc<RefCell<T>> adalah blok penyusun bersama-mutable single-thread yang idiomatik.
Bagaimana penghitungan referensi bisa membocorkan memori di Rust? Dua nilai Rc (atau Arc) yang saling mereferensikan membentuk sebuah siklus yang hitungan kuatnya tidak pernah mencapai nol, sehingga alokasinya tidak pernah dibebaskan. Memutus siklus dengan Weak<T>, yang memegang referensi non-pemilik, memulihkan pembersihan yang benar.
Kapan Box lebih disukai daripada Rc? Kapan pun satu pemilik sudah cukup. Box tidak memiliki overhead penghitungan referensi, sehingga menjadi pilihan default untuk alokasi heap, tipe rekursif, dan trait object. Gunakan Rc hanya ketika kepemilikan bersama yang sesungguhnya diperlukan.
Kesimpulan
Smart pointer Rust mengubah kepemilikan dari sebuah batasan menjadi seperangkat blok penyusun yang dapat dikomposisi. Pilihan di antara mereka mengikuti langsung dari bentuk masalahnya:
- Gunakan
Box<T>ketika sebuah nilai membutuhkan pemilik heap tunggal, sebuah tipe rekursif membutuhkan ukuran yang pasti, atau sebuah fungsi mengembalikan trait object. - Gunakan
Rc<T>untuk banyak pemilik pada satu thread, dan beralihlah keArc<T>begitu kepemilikan melintasi batas thread. - Tambahkan
RefCell<T>untuk mutabilitas interior dalam kode single-thread, danMutex<T>untuk padanan konkurennya, dengan menjaga guard peminjaman dan penguncian tetap berumur pendek. - Gabungkan secara sengaja:
Rc<RefCell<T>>untuk keadaan bersama yang dapat diubah pada satu thread,Arc<Mutex<T>>antar-thread. - Waspadai siklus referensi antar pointer terhitung dan putuskan dengan
Weak<T>untuk menghindari kebocoran. - Default-kan ke tipe paling tidak berkuasa dan biarkan kompiler memberi tahu kapan lebih banyak kemampuan diperlukan.
Mulai berlatih!
Uji pengetahuan Anda dengan simulator wawancara dan tes teknis kami.
Tag
Bagikan
Artikel terkait

Memahami Ownership dan Borrowing di Rust: Panduan Lengkap untuk Developer
Pelajari konsep ownership, borrowing, dan lifetime di Rust secara mendalam. Artikel ini membahas aturan kepemilikan, referensi, serta pola-pola umum yang sering muncul dalam wawancara teknis.

Rust 2026: Traits, Generics, dan Pertanyaan Wawancara Tingkat Lanjut
Panduan mendalam tentang traits dan generics Rust dengan fitur terbaru edisi 2024: trait upcasting, AsyncFn, RPITIT, serta pertanyaan wawancara teknis yang sering diujikan.

Pertanyaan Wawancara Rust: Panduan Lengkap 2026
25 pertanyaan wawancara Rust yang paling sering ditanyakan. Ownership, borrowing, lifetime, trait, async dan concurrency dengan jawaban detail serta contoh kode.