Lewati ke konten

Error Rust E0434: can't capture dynamic environment in a fn item (fn di dalam fn)

E0434 artinya: fungsi yang ditulis di dalam fungsi lain mencoba memakai salah satu variabel lokal fungsi luarnya. Kelihatannya seharusnya bisa: variabelnya ada di situ, beberapa baris di atasnya. Tapi fn bersarang di Rust adalah item yang terpisah, dan ia tidak bisa melihat variabel di sekitarnya. Istilah dari compiler, “dynamic environment” (lingkungan dinamis), sebenarnya cuma berarti “variabel lokal yang ada selama fungsi luarnya berjalan”. Ini salah satu error yang paling gampang diperbaiki: rustc sudah memberi jawabannya di baris help:. (Halaman ini bagian dari Penjelasan error compiler Rust.)

error[E0434]: can't capture dynamic environment in a fn item
--> src/main.rs:5:24
|
5 | price * (1.0 + tax_rate) // ❌ inner `fn` can't see `tax_rate`
| ^^^^^^^^
|
= help: use the `|| { ... }` closure form instead

Artinya: item fn tidak bisa menangkap lingkungan dinamis (variabel lokal). Help-nya menyarankan memakai bentuk closure || { ... }.

Ini program yang menghasilkan error tersebut:

fn main() {
let tax_rate = 0.2;
fn with_tax(price: f64) -> f64 {
price * (1.0 + tax_rate) // ❌ inner `fn` can't see `tax_rate`
}
println!("{:.2}", with_tax(10.0));
}

Tanda ^^^ menunjuk ke tax_rate di baris 5. Itu variabel lokal milik main, dibuat dengan let di baris 2. Fungsi di dalamnya, with_tax, mencoba membacanya, dan itu tidak diizinkan.

Baris help: memberi perbaikannya: tulis sebuah closure, || { ... }, sebagai ganti fn bersarang. Closure bisa memakai variabel di sekitarnya. fn tidak bisa, di mana pun kamu menulisnya.

Sering kamu juga mendapat warning, unused variable: `tax_rate` (variabel tax_rate tidak dipakai), karena dari sudut pandang Rust tidak ada yang boleh memakainya. Warning itu ikut hilang bersama error-nya.

Ibaratnya surat bersegel yang kamu tulis di mejamu. Tidak penting bahwa catatanmu ada di meja saat kamu menulisnya; orang yang membukanya hanya melihat apa yang kamu masukkan ke dalam amplop.

fn bersarang di-compile persis seperti fungsi di bagian atas file. Menulisnya di dalam main hanya menyembunyikan namanya dari bagian kode lainnya. Itu tidak menghubungkannya dengan variabel main, dan ada alasan bagus untuk itu: fungsi biasa tidak punya tempat untuk menyimpannya. Kamu bisa memanggil with_tax dari mana saja yang bisa dijangkau main, menyimpannya sebagai function pointer, atau memanggilnya setelah variabel main sudah tidak ada. fn biasa hanyalah kode, tanpa data yang menempel.

Closure adalah kode ditambah variabel yang ditangkapnya. Rust menyimpan nilai-nilai itu (atau referensinya) di dalam closure, dan borrow checker memastikan semuanya hidup cukup lama. Itulah sebabnya rustc mengarahkanmu ke closure. Lihat Menangkap variabel dari scope di sekitarnya.

Aturan yang sama berlaku untuk parameter generic: fn di dalam juga tidak bisa memakai T milik fungsi luarnya. Yang itu adalah E0401.

Pilih ini hampir setiap saat, kalau fungsi bantunya kecil dan hanya hidup di dalam satu fungsi. Tulis let nama = |params| isi;, dan closure itu bisa memakai variabel apa pun di scope:

fn main() {
let tax_rate = 0.2;
let with_tax = |price: f64| price * (1.0 + tax_rate); // ✅ closure bisa melihat `tax_rate`
println!("{:.2}", with_tax(10.0));
println!("{:.2}", with_tax(24.5));
let prices = [3.0, 7.5, 12.0];
let total: f64 = prices.iter().map(|&p| with_tax(p)).sum();
println!("total: {:.2}", total);
}

Output:

12.00
29.40
total: 27.00
▶ Coba sendiri

Closure yang hanya membaca sebuah variabel akan meminjamnya, jadi kamu masih bisa memakai tax_rate sesudahnya. Kalau kamu mengubah variabel itu di dalam closure, closure-nya butuh let mut. Lihat Closure.

Perbaikan 2: Kirim nilainya sebagai argumen

Section titled “Perbaikan 2: Kirim nilainya sebagai argumen”

Pilih ini kalau fungsi bantunya sebaiknya tetap menjadi fungsi sungguhan: lebih panjang, ingin kamu test, atau dipakai beberapa fungsi. Beri satu parameter lagi dan kirim nilainya:

fn with_tax(price: f64, tax_rate: f64) -> f64 {
price * (1.0 + tax_rate) // ✅ `tax_rate` sekarang sebuah parameter
}
fn receipt(prices: &[f64], tax_rate: f64) -> f64 {
prices.iter().map(|&p| with_tax(p, tax_rate)).sum()
}
fn main() {
let tax_rate = 0.2;
println!("{:.2}", with_tax(10.0, tax_rate));
println!("total: {:.2}", receipt(&[3.0, 7.5, 12.0], tax_rate));
let reduced_rate = 0.05;
println!("{:.2}", with_tax(10.0, reduced_rate));
}

Output:

12.00
total: 27.00
10.50
▶ Coba sendiri

Di sini fungsinya pindah ke level teratas, tempat sebagian besar fungsi bantu Rust berada. Tetap bersarang juga bisa; yang penting nilainya masuk sebagai argumen. Bonusnya: sekarang fungsi ini bekerja untuk tarif pajak berapa pun, bukan cuma satu. Lihat Parameter.

Pilih ini kalau nilainya tetap dan sudah diketahui sebelum program berjalan, seperti tarif pajak yang ditulis langsung di kode. Berbeda dengan variabel let, const adalah sebuah item, dan fungsi mana pun bisa memakainya, bersarang atau tidak:

const TAX_RATE: f64 = 0.2; // ✅ konstanta, terlihat oleh setiap fungsi
fn main() {
fn with_tax(price: f64) -> f64 {
price * (1.0 + TAX_RATE)
}
println!("{:.2}", with_tax(10.0));
println!("{:.2}", with_tax(24.5));
}

Output:

12.00
29.40
▶ Coba sendiri

Ini hanya bisa untuk nilai yang bisa dihitung saat compile. Kalau tarifnya berasal dari file, input pengguna, atau variabel lain, const tidak bisa dipakai: const RATE: f64 = tax_rate; memberi error lain, E0435 (attempt to use a non-constant value in a constant, mencoba memakai nilai non-konstan di dalam konstanta). Kalau begitu, pakai Perbaikan 1 atau Perbaikan 2. Lihat Konstanta.

Kalau kamu datang dari JavaScript atau Python. Di bahasa-bahasa itu, fungsi bersarang bisa melihat variabel luarnya, jadi fn di dalam fn terasa wajar. Di Rust, yang bisa melakukan itu adalah closure. Perbaikannya: let helper = |x| ...; sebagai ganti fn helper(x) ....

Memakai self di fn bersarang di dalam method. fn report(&self) { fn show() { println!("{}", self.n); } } memberi E0434 yang sama, dengan tanda menunjuk ke self. Perbaikannya: jadikan show sebuah method (fn show(&self) di dalam impl) lalu panggil self.show(), atau pakai closure.

Parameter fungsi, bukan let. Di fn total(prices: &[f64], discount: f64), fn apply(price: f64) -> f64 { price - discount } yang bersarang juga memberi E0434: parameter juga termasuk variabel lokal. Perbaikannya: kirim discount ke apply, atau tulis let apply = |price: f64| price - discount;.

Kuis singkat

1. Di dalam main, ada let limit = 10; dan fn check(n: i32) -> bool { n < limit } yang bersarang. Apa yang terjadi?

2. Versi mana yang bisa memakai variabel lokal limit?

3. Nilainya berasal dari input pengguna saat program berjalan. Perbaikan mana yang TIDAK akan berhasil?

Skor: 0 / 3