Error Rust E0384: cannot assign twice to immutable variable (diisi dua kali)
E0384 artinya kamu sudah memberi sebuah variabel nilai, lalu mencoba memberinya nilai lain, padahal variabel itu tidak dideklarasikan dengan mut. Di Rust, variabel tidak bisa diubah kecuali kamu sendiri yang bilang boleh. Hampir semua orang kena error ini di jam pertama belajar Rust, dan perbaikannya biasanya cuma tiga huruf: mut. Halaman ini menunjukkan perbaikannya dulu, lalu menjelaskan kenapa Rust bekerja seperti ini. Untuk error lain di seri ini, lihat Error compiler Rust.
Pesan error-nya
Section titled “Pesan error-nya”error[E0384]: cannot assign twice to immutable variable `score` --> src/main.rs:4:5 |2 | let score = 10; | ----- first assignment to `score`3 | println!("Score: {score}");4 | score = 15; // ❌ score tidak mut | ^^^^^^^^^^ cannot assign twice to immutable variable |help: consider making this binding mutable |2 | let mut score = 10; | +++Ini program yang menghasilkannya:
fn main() { let score = 10; println!("Score: {score}"); score = 15; // ❌ score tidak mut println!("New score: {score}");}Kalau variabelnya adalah parameter fungsi, kalimatnya sedikit berbeda: “cannot assign to immutable argument” (tidak bisa mengisi ulang argumen yang immutable). Kode error-nya sama, idenya juga sama.
Apa artinya
Section titled “Apa artinya”Compiler menunjuk dua tempat:
- Baris 2, ditandai
first assignment to score(pengisian pertamascore): di sinilahscoremendapat nilainya. Karena barisnya ditulislet, bukanlet mut, nilai itu sudah final. - Baris 4, ditandai
^^^^: di sinilah kamu mencoba memasukkan nilai baru. Rust menolaknya.
Bagian help: sudah menunjukkan perbaikannya: let mut score = 10;. Tanda +++ menandai tiga karakter yang perlu ditambahkan.
Bayangkan let itu menulis dengan pulpen, dan let mut menulis dengan pensil. Dua-duanya boleh. Tapi kalau kamu menulis dengan pulpen, kamu tidak bisa menghapusnya nanti.
Aturan yang sama berlaku untuk +=, -=, *= dan teman-temannya. count += 1 juga termasuk pengisian nilai, jadi juga butuh mut.
Kenapa Rust melakukan ini
Section titled “Kenapa Rust melakukan ini”Di Rust, setiap variabel immutable secara default (tidak dapat diubah). Kamu harus memilih sendiri agar bisa diubah dengan mut. Ini dijelaskan di halaman Variabel.
Kedengarannya ketat, tapi ini sangat membantu. Saat kamu membaca let total = ..., kamu tahu total akan tetap bernilai itu sampai akhir scope-nya. Kamu tidak perlu menelusuri sisa fungsi untuk memastikan tidak ada yang mengubahnya. Dan saat kamu melihat let mut, itu tanda yang jelas: “perhatikan yang ini, nilainya berubah”.
Ini juga menangkap bug sungguhan. Kalau kamu berniat mengubah salinan tapi tidak sengaja menulis ke aslinya, atau kamu memakai ulang sebuah nama secara tidak sengaja, compiler akan menghentikanmu. Rust juga memberi peringatan untuk variabel mut yang tidak pernah berubah (variable does not need to be mutable), jadi tanda mut tetap jujur dari dua arah.
Cara memperbaikinya
Section titled “Cara memperbaikinya”Perbaikan 1: Tambahkan mut
Section titled “Perbaikan 1: Tambahkan mut”Pilih ini kalau variabelnya memang seharusnya berubah dari waktu ke waktu: penghitung, total berjalan, skor, atau status. Ini perbaikan yang tepat di sebagian besar kasus, dan persis inilah yang disarankan compiler.
fn main() { let mut score = 10; println!("Skor: {score}"); score = 15; // ✅ sekarang boleh println!("Skor baru: {score}");
let mut count = 0; for _ in 0..3 { count += 1; } println!("Jumlah: {count}");}Output:
Skor: 10Skor baru: 15Jumlah: 3Penghitung adalah contoh klasiknya: let count = 0; lalu count += 1; di dalam perulangan memberi E0384 di baris +=. Menambahkan mut memperbaikinya. Lihat Membuat variabel bisa diubah dengan mut.
Perbaikan 2: Pakai shadowing untuk nilai baru
Section titled “Perbaikan 2: Pakai shadowing untuk nilai baru”Pilih ini kalau kamu sebenarnya tidak ingin mengubah variabelnya. Kamu ingin nilai baru yang dihitung dari nilai lama, sering kali dalam beberapa langkah, dan setelah itu nilainya tetap. Tulis let lagi dengan nama yang sama:
fn main() { let price = 20; let price = price + 5; // `price` baru, dibuat dari yang lama let price = price * 2; println!("Harga akhir: {price}");
let input = " 42 "; let input = input.trim(); // masih berupa teks let input: u32 = input.parse().unwrap(); // sekarang berupa angka println!("Hasil parse: {}", input + 1);}Output:
Harga akhir: 50Hasil parse: 43Ini disebut shadowing (menimpa nama): setiap let membuat variabel yang benar-benar baru dan menyembunyikan yang lama. Ada dua keuntungan. price yang terakhir immutable, jadi tidak ada yang bisa mengubahnya secara tidak sengaja nanti. Dan tipenya bisa berubah, seperti input yang berubah dari &str menjadi u32, sesuatu yang tidak bisa dilakukan mut. Selengkapnya di Shadowing: memakai ulang nama yang sama.
Aturan sederhananya: kalau nilainya berubah terus-menerus (di dalam perulangan, seiring waktu), pakai mut. Kalau kamu sedang menyiapkan sebuah nilai dalam beberapa langkah, shadowing lebih enak dibaca.
Perbaikan 3: Tambahkan mut ke parameter fungsi
Section titled “Perbaikan 3: Tambahkan mut ke parameter fungsi”Pilih ini kalau sebuah fungsi ingin mengubah salinan argumennya sendiri. Parameter juga immutable, jadi kode ini gagal:
fn countdown(n: u32) { while n > 0 { println!("{n}..."); n -= 1; // ❌ cannot assign to immutable argument `n` } println!("Liftoff!");}error[E0384]: cannot assign to immutable argument `n` --> src/main.rs:4:9 |4 | n -= 1; // ❌ cannot assign to immutable argument `n` | ^^^^^^ cannot assign to immutable argument |help: consider making this binding mutable |1 | fn countdown(mut n: u32) { | +++Compiler menunjuk n -= 1 dan menyarankan mut n di parameter. Taruh mut di depan nama parameternya:
fn countdown(mut n: u32) { while n > 0 { println!("{n}..."); n -= 1; } println!("Meluncur!");}
fn main() { let start = 3; countdown(start); println!("start masih {start}");}Output:
3...2...1...Meluncur!start masih 3Perhatikan bahwa start di main tidak berubah. mut n hanya membuat salinan milik fungsi itu sendiri bisa diubah; pemanggilnya tidak perlu mut dan tidak terpengaruh. Kalau kamu ingin fungsinya mengubah variabel milik pemanggil, terima &mut u32 dan kirim &mut start. Lihat Fungsi dan Borrowing.
Di mana biasanya kamu melihatnya
Section titled “Di mana biasanya kamu melihatnya”Mengubah variabel perulangan for. Variabel perulangan adalah variabel baru yang immutable di setiap putaran:
for i in 0..5 { if i == 2 { i += 1; // ❌ cannot assign twice to immutable variable `i` } println!("{i}");}Perbaikan: kamu jarang benar-benar ingin mengubah penghitung perulangannya. Lompati putaran dengan continue, pakai .step_by(2), atau kalau kamu butuh kendali penuh, ganti dengan perulangan while dan let mut i. (for mut i in 0..5 memang bisa dikompilasi, tapi itu hanya mengubah salinan di putaran itu; tidak ada yang terlewati.) Lihat break dan continue dan perulangan while.
Mendeklarasikan dulu, mengisi belakangan. let total; tanpa nilai itu boleh, dan kamu bisa mengisinya sekali. Pengisian kedua memberi E0384:
let total;total = 5;total = 6; // ❌ cannot assign twice to immutable variable `total`Perbaikan: kalau nilainya memang berubah, pakai let mut total. Kalau kamu sebenarnya hanya ingin memilih satu nilai, hitung dalam satu ekspresi: let total = if big_order { 6 } else { 5 };. Lihat if / else.
Mengumpulkan total. let sum = 0; lalu sum += price; di dalam perulangan adalah E0384 yang paling sering terjadi. Perbaikan: let mut sum = 0;, atau lewati variabelnya dan tulis let sum: u32 = prices.iter().sum();. Lihat Iterator.
Error terkait
Section titled “Error terkait”- E0596: cannot borrow as mutable, as it is not declared as mutable (tidak bisa meminjam sebagai mutable). Masalah “lupa
mut” yang sama, tapi untuk memanggil method yang mengubah seperti.push(), bukan untuk mengisi nilai. - E0594: cannot assign to
p.hp, aspis not declared as mutable. Kamu melihat ini saat mengisi field struct (p.hp = 5;) atau menulis lewat referensi&. Perbaikan:let mut p, atau referensi&mut. Lihat Membuat struct menjadi mutable. - E0502: kamu mencoba mengubah sebuah nilai saat nilai itu sedang dipinjam untuk dibaca.
- E0382: kamu memakai sebuah nilai setelah nilai itu dipindah (move).
- Semua error compiler: daftar lengkap halaman error.
Kuis singkat
Section titled “Kuis singkat”Kuis singkat
1. Kamu menulis let lives = 3; lalu kemudian lives -= 1;. Kenapa kamu mendapat E0384?
2. Kamu ingin merapikan name sekali saja (trim lalu jadikan huruf besar) dan tidak pernah mengubahnya lagi. Mana yang paling enak dibaca?
3. fn halve(n: u32) menjalankan n /= 2; dan mendapat E0384. Perbaikan apa yang membuat pemanggilnya tetap sama?
Skor: 0 / 3
Bacaan lebih lanjut
Section titled “Bacaan lebih lanjut”- E0384 — Rust error codes index (bahasa Inggris): penjelasan resmi error ini, lengkap dengan contoh.
- Variables and Mutability — The Rust Book (bahasa Inggris): bab tentang
let,mut, konstanta, dan shadowing. - Mutability — Rust by Example (bahasa Inggris): contoh singkat yang bisa dijalankan tentang
letdibandingkanlet mut. - Freezing — Rust by Example (bahasa Inggris): bagaimana shadowing variabel mutable dengan variabel immutable “membekukan” nilainya.