Error Rust E0382: borrow of moved value (nilai sudah dipindah)
E0382 artinya kamu sudah menyerahkan sebuah nilai (kamu memindahkannya, istilahnya move), lalu mencoba memakainya lagi. Ini error yang paling sering ditemui orang di minggu pertama belajar Rust, dan hampir selalu bisa diperbaiki dengan satu baris. Halaman ini menunjukkan perbaikannya dulu, baru kemudian menjelaskan apa yang terjadi. Untuk error lain di seri ini, lihat Penjelasan error compiler Rust.
Pesan error-nya
Section titled “Pesan error-nya”error[E0382]: borrow of moved value: `name` --> src/main.rs:8:25 |6 | let name = String::from("Ferris"); | ---- move occurs because `name` has type `String`, which does not implement the `Copy` trait7 | greet(name); // name di-move ke dalam greet di sini | ---- value moved here8 | println!("Goodbye, {name}!"); // ❌ name sudah tidak ada | ^^^^ value borrowed here after move |note: consider changing this parameter type in function `greet` to borrow instead if owning the value isn't necessary --> src/main.rs:1:16 |1 | fn greet(name: String) { | ----- ^^^^^^ this parameter takes ownership of the valuehelp: consider cloning the value if the performance cost is acceptable |7 | greet(name.clone()); // name di-move ke dalam greet di sini | ++++++++Compiler menyebut tiga hal: nilainya di-move di baris 7, lalu dipinjam lagi di baris 8. Ia juga menyarankan dua jalan keluar: ubah parameter greet menjadi borrow, atau clone nilainya.
Ini program yang menghasilkan error tersebut:
fn greet(name: String) { println!("Hello, {name}!");}
fn main() { let name = String::from("Ferris"); greet(name); // name di-move ke dalam greet di sini println!("Goodbye, {name}!"); // ❌ name sudah tidak ada}Kamu mungkin juga melihat kalimat “use of moved value”. Itu error yang sama. Rust bilang “borrow” kalau baris berikutnya hanya membaca nilainya (seperti println!), dan “use” kalau baris itu mencoba memindahkannya lagi (seperti let other = name;).
Apa artinya
Section titled “Apa artinya”Baca tiga label itu dari atas ke bawah. Ketiganya menceritakan semuanya:
- Baris 6:
nameadalahString, danStringbukanCopy. Jadi kalau diberikan ke tempat lain, ia dipindahkan (move). - Baris 7:
greet(name)memindahkan string itu ke dalam fungsi. Sekaranggreetyang memilikinya, dan string itu dibuang saatgreetselesai. - Baris 8: kamu mencoba membaca
namelagi, padahalmainsudah tidak memilikinya.
Bayangkan kamu memberikan satu-satunya kunci rumahmu ke seorang teman. Begitu sudah diserahkan, kamu tidak bisa membuka pintu dengan kunci itu, meskipun kamu ingat dulu pernah memegangnya.
Kenapa Rust melakukan ini
Section titled “Kenapa Rust melakukan ini”Setiap nilai di Rust punya tepat satu owner (pemilik). Saat owner-nya hilang, nilainya dibersihkan. Dengan cara inilah Rust membebaskan memori tanpa garbage collector. Aturannya dijelaskan di halaman Ownership (kepemilikan).
Kalau dua variabel sama-sama memiliki String yang sama, keduanya akan mencoba membebaskan memori yang sama di akhir. Itu namanya “double free”, bug sungguhan yang bisa membuat program C dan C++ crash. Rust mencegahnya dengan membuat assignment dan pemanggilan fungsi memindahkan nilai, sehingga selalu hanya ada satu owner.
Tipe kecil dan sederhana seperti i32, bool, dan char adalah Copy: saat diberikan ke tempat lain, yang terjadi adalah salinan murah, dan kamu tidak akan pernah melihat E0382 untuk tipe-tipe itu. Tipe yang memiliki memori di heap, seperti String, Vec, dan kebanyakan struct, dipindahkan.
Cara memperbaikinya
Section titled “Cara memperbaikinya”Perbaikan 1: Pinjam dengan &
Section titled “Perbaikan 1: Pinjam dengan &”Pilih ini kalau fungsinya hanya perlu melihat nilainya. Hampir selalu inilah perbaikan yang tepat. Ubah parameternya menjadi referensi dan berikan &name:
fn greet(name: &str) { println!("Halo, {name}!");}
fn main() { let name = String::from("Ferris"); greet(&name); // pinjamkan, jangan diserahkan println!("Sampai jumpa, {name}!"); // ✅ masih milik kita}Output:
Halo, Ferris!Sampai jumpa, Ferris!&String juga bisa, tapi &str lebih umum dipakai karena juga menerima string literal. Lihat Borrowing (peminjaman) dan String. Kalau fungsinya perlu mengubah nilainya, pakai &mut.
Perbaikan 2: Clone nilainya
Section titled “Perbaikan 2: Clone nilainya”Pilih ini kalau kode lain memang butuh salinannya sendiri, misalnya untuk disimpan di struct atau Vec, sementara kamu masih butuh yang asli. .clone() membuat salinan penuh yang berdiri sendiri:
struct Player { name: String, score: u32,}
fn main() { let name = String::from("Ferris"); let player = Player { name: name.clone(), score: 0 }; println!("{} mulai dengan {} poin", player.name, player.score); println!("Nama asli: {name}");}Output:
Ferris mulai dengan 0 poinNama asli: FerrisJujur saja: meng-clone string pendek atau Vec kecil itu murah, dan sama sekali tidak apa-apa, apalagi selagi kamu masih belajar. Baru pikirkan soal ini kalau kamu meng-clone data besar di dalam loop yang sangat sering berjalan. Kalau borrow (Perbaikan 1) sudah cukup, pilih itu.
Perbaikan 3: Derive Copy untuk tipe kecil
Section titled “Perbaikan 3: Derive Copy untuk tipe kecil”Pilih ini untuk struct kecil yang hanya berisi nilai sederhana, seperti koordinat, warna, atau ID. Tambahkan #[derive(Clone, Copy)], dan struct itu akan disalin, bukan dipindahkan:
#[derive(Debug, Clone, Copy)]struct Point { x: i32, y: i32,}
fn show(p: Point) { println!("({}, {})", p.x, p.y);}
fn main() { let start = Point { x: 0, y: 3 }; show(start); // menyalin start show(start); // ✅ bisa dipakai lagi println!("{:?}", start);}Output:
(0, 3)(0, 3)Point { x: 0, y: 3 }Copy hanya bisa dipakai kalau setiap field juga Copy. Struct dengan field String atau Vec tidak bisa Copy; untuk itu pakai Clone dan panggil .clone(). Selengkapnya di Trait yang sering dipakai.
Perbaikan 4: Kembalikan ownership-nya
Section titled “Perbaikan 4: Kembalikan ownership-nya”Pilih ini kalau sebuah fungsi menerima nilai, mengubahnya, dan kamu ingin hasilnya. Fungsi itu mengembalikan nilainya, lalu kamu menyimpannya lagi:
fn add_signature(mut letter: String) -> String { letter.push_str("\n-- Ferris"); letter // kembalikan}
fn main() { let letter = String::from("Halo, Rustacean,"); let letter = add_signature(letter); // ambil kembali println!("{letter}");}Output:
Halo, Rustacean,-- FerrisCara ini cocok untuk fungsi bergaya “builder”. Untuk perubahan sederhana di tempat, fn add_signature(letter: &mut String) sering lebih mudah dibaca.
Di mana biasanya kamu melihatnya
Section titled “Di mana biasanya kamu melihatnya”Loop for atas sebuah Vec. for task in tasks memindahkan seluruh vektor ke dalam loop, jadi kamu tidak bisa memakai tasks sesudahnya:
for task in tasks { println!("Doing: {task}");}println!("{} tasks done", tasks.len()); // ❌ `tasks` moved due to this implicit call to `.into_iter()`Perbaikannya: loop atas sebuah borrow, for task in &tasks. Lihat For di Rust dan Iterator.
Memasukkan nilai ke Vec atau struct. names.push(name) dan Order { customer } sama-sama memindahkan nilainya ke dalam. Memakai name atau customer sesudahnya akan gagal. Perbaikannya: baca lagi dari “rumah barunya” (names[0], order.customer), kerjakan hal lain sebelum move, atau push hasil .clone().
Move di dalam loop. Kalau log.push(message) ada di dalam loop, putaran pertama memindahkan message, dan putaran kedua sudah tidak punya apa-apa. Compiler akan bilang value moved here, in previous iteration of loop (nilai sudah dipindah di putaran sebelumnya). Perbaikannya: push message.clone(), atau buat nilainya di dalam loop supaya setiap putaran punya nilai baru.
Thread dan closure move. thread::spawn(move || ...) memindahkan setiap variabel yang ditangkap ke thread baru. Perbaikannya: clone datanya sebelum spawn, atau bagikan dengan Arc. Lihat Konkurensi dan Shared state.
Error terkait
Section titled “Error terkait”- E0502: kamu meminjam sebuah nilai, lalu mencoba mengubahnya selagi borrow itu masih dipakai.
- E0499: dua borrow mutable atas nilai yang sama pada saat bersamaan.
- E0106: lifetime specifier tidak ada, sering muncul saat kamu mengembalikan referensi untuk menghindari move.
- E0308: tipe tidak cocok, misalnya memberikan
Stringpadahal yang diharapkan&strsetelah kamu beralih ke borrow. - Semua error compiler: daftar lengkap halaman error.
Kuis singkat
Section titled “Kuis singkat”Kuis singkat
1. Kamu memanggil save(report) dengan report bertipe String, lalu mencetak report. Kenapa muncul E0382?
2. Sebuah fungsi hanya perlu membaca Vec<String>. Biasanya apa perbaikan terbaiknya?
3. Struct mana yang bisa memakai #[derive(Clone, Copy)]?
Skor: 0 / 3
Bacaan lebih lanjut
Section titled “Bacaan lebih lanjut”- E0382 — Rust error codes index (bahasa Inggris): penjelasan resmi error ini, dengan contoh tambahan.
- What Is Ownership? — The Rust Book (bahasa Inggris): bab yang menjelaskan move,
Clone, danCopylangkah demi langkah. - References and Borrowing — The Rust Book (bahasa Inggris): bagaimana
&membuatmu bisa memakai nilai tanpa memindahkannya. - Ownership and moves — Rust by Example (bahasa Inggris): contoh singkat yang bisa dijalankan tentang move dan partial move.
Copy— dokumentasi standard library (bahasa Inggris): tipe mana yangCopy, dan beda antaraCopydanClone.