Scope di Rust
Scope (cakupan) adalah bagian kode tempat sebuah variabel berlaku. Idenya sederhana, tapi kalau kamu memahaminya, banyak aturan Rust lainnya (terutama ownership) jadi jauh lebih masuk akal.
Scope ditentukan oleh kurung kurawal
Section titled “Scope ditentukan oleh kurung kurawal”Sebuah variabel ada sejak dideklarasikan sampai tanda } penutup dari blok tempat ia berada:
fn main() { let x = 5; // x masuk ke scope di sini
{ let y = 10; // y masuk ke scope di sini println!("x = {x}, y = {y}"); // keduanya berlaku } // y keluar dari scope di sini
println!("x = {x}"); // println!("{y}"); // ❌ error: y sudah tidak ada di scope}Output:
x = 5, y = 10x = 5y hanya ada di dalam blok { } bagian dalam. Begitu blok itu berakhir, y hilang. Mencoba memakainya setelah itu menghasilkan error saat kompilasi, bukan kejutan saat program berjalan.
Scope hanya berlaku satu arah: blok dalam bisa melihat variabel dari blok-blok di sekitarnya (itulah kenapa blok dalam bisa mencetak x), tapi blok luar tidak bisa melihat ke dalam blok dalam.
Isi fungsi juga sebuah scope
Section titled “Isi fungsi juga sebuah scope”Parameter dan variabel apa pun yang kamu buat di dalam sebuah fungsi hanya ada selama pemanggilan fungsi itu:
fn calculate() -> i32 { let result = 5 * 2; // result hanya ada di dalam calculate result}
fn main() { let value = calculate(); println!("{value}"); // println!("{result}"); // ❌ error: result tidak ada di sini}Output:
10Hal yang sama berlaku sebaliknya: sebuah fungsi tidak bisa melihat variabel lokal milik main. Kalau fungsi itu butuh sebuah nilai, kamu mengirimnya sebagai parameter, seperti yang sudah kamu lihat di halaman Fungsi.
Blok juga sebuah ekspresi
Section titled “Blok juga sebuah ekspresi”Blok { } tidak hanya membatasi tempat variabel hidup. Blok juga bisa menghasilkan nilai: ekspresi terakhir di dalamnya, yang ditulis tanpa titik koma. Ini aturan yang sama yang membuat sebuah fungsi mengembalikan baris terakhirnya.
Ini berguna ketika kamu butuh beberapa variabel bantu untuk menghitung sesuatu, tapi tidak ingin variabel itu tetap berkeliaran sesudahnya:
fn main() { let price = 40;
let total = { let tax = price / 10; // tax hanya hidup di dalam blok ini let shipping = 5; price + tax + shipping // tanpa titik koma: ini nilai bloknya };
println!("total = {total}"); // println!("{tax}"); // ❌ error: tax sudah di luar scope}Output:
total = 49Ada dua detail yang perlu diperhatikan:
- Baris terakhir
price + tax + shippingtidak memakai titik koma. Kalau kamu menambahkannya, blok itu mengembalikan()(tidak ada apa-apa) alih-alih angka, danprintln!tidak bisa mencetaknya. - Semuanya adalah satu pernyataan
let, jadi diakhiri dengan};setelah kurung kurawal penutup.
Begitu bloknya berakhir, tax dan shipping hilang. Yang tersisa hanya hasilnya, total. Menaruh variabel bantu di blok kecil seperti ini membuat jelas bahwa variabel itu hanya dibutuhkan untuk satu perhitungan.
Kenapa scope penting untuk ownership
Section titled “Kenapa scope penting untuk ownership”Ini berhubungan langsung dengan ownership (kepemilikan), yang akan kamu temui sebentar lagi: sebuah nilai otomatis dibersihkan begitu scope pemiliknya berakhir.
fn main() { { let text = String::from("halo"); println!("{text}"); } // scope text berakhir di sini — Rust otomatis membersihkannya (drop)}Output:
haloItulah seluruh mekanisme yang membuat Rust tidak butuh garbage collector: saat kompilasi, Rust sudah tahu persis di mana setiap scope berakhir.
Shadowing dan scope
Section titled “Shadowing dan scope”Masih ingat shadowing dari halaman Variabel? Setiap let dengan nama yang sama di dalam sebuah scope membuat variabel yang berbeda. Variabel itu tidak menghapus variabel luar, ia hanya mengambil alih untuk sisa scope tersebut:
fn main() { let x = 5; { let x = x * 2; // x baru, hanya di dalam blok ini println!("x dalam = {x}"); // 10 } println!("x luar = {x}"); // 5 — tidak terpengaruh}Output:
x dalam = 10x luar = 5Latihan
Section titled “Latihan”1. Perbaiki error scope
Section titled “1. Perbaiki error scope”Program ini tidak bisa dikompilasi, karena area dipakai setelah bloknya berakhir:
fn main() { let width = 3; { let height = 4; let area = width * height; } println!("luas = {area}");}Ubah supaya program mencetak luas = 12, sementara height tetap hanya hidup di dalam blok. Petunjuk: buat bloknya menghasilkan luas, lalu simpan hasilnya di variabel yang dideklarasikan di luar blok.
Lihat solusi
fn main() { let width = 3; let area = { let height = 4; width * height }; println!("luas = {area}");}Output:
luas = 12Sekarang area hidup di scope main, jadi println! bisa melihatnya. height tetap hanya dibutuhkan di dalam blok, dan ia dibersihkan saat bloknya berakhir.
2. Tingkat-tingkat blok bersarang
Section titled “2. Tingkat-tingkat blok bersarang”Buat variabel level dengan nilai 1. Buka sebuah blok yang men-shadow-nya dengan 2, dan di dalamnya, blok lain yang men-shadow-nya dengan 3. Cetak level di setiap blok, dan cetak lagi tepat setelah setiap blok ditutup. Petunjuk: kamu seharusnya melihat angkanya naik 1, 2, 3, lalu turun lagi ke 2 dan 1.
Lihat solusi
fn main() { let level = 1; println!("level {level}"); { let level = 2; println!("level {level}"); { let level = 3; println!("level {level}"); } println!("kembali ke level {level}"); } println!("kembali ke level {level}");}Output:
level 1level 2level 3kembali ke level 2kembali ke level 1Setiap level di dalam hanya menyembunyikan level di luarnya sampai bloknya berakhir. Variabel-variabel luar tidak pernah diubah, jadi mereka masih ada di sana, menunggu.
Ringkasan
Section titled “Ringkasan”- Scope adalah bagian kode (ditandai
{ }) tempat sebuah variabel berlaku. - Sebuah variabel berhenti ada tepat saat
}penutup scope-nya tercapai. - Isi fungsi juga sebuah scope: variabel lokal di dalam fungsi hilang saat fungsi itu selesai.
- Blok adalah ekspresi: baris terakhirnya (tanpa titik koma) adalah nilainya, dan variabel bantunya lenyap saat blok berakhir.
- Scope-lah yang memberi tahu Rust kapan tepatnya sebuah nilai dibersihkan, makanya tidak perlu garbage collector.
Kuis singkat
Section titled “Kuis singkat”Kuis singkat
1. Menurut halaman ini, kenapa Rust tidak butuh garbage collector?
2. Apa yang terjadi kalau kamu men-shadow sebuah variabel dengan let x = x * 2; di dalam blok dalam?
3. Berapa nilai total setelah let total = { let a = 2; a * 10 };?
Skor: 0 / 3
Bacaan lebih lanjut
Section titled “Bacaan lebih lanjut”- Variable Scope — The Rust Book (bahasa Inggris): cara kerja scope, dijelaskan sebagai langkah pertama menuju ownership.
- Scope and Shadowing — Rust by Example (bahasa Inggris): contoh singkat yang bisa dijalankan tentang blok dan shadowing.
- RAII — Rust by Example (bahasa Inggris): apa yang terjadi pada nilai saat scope-nya berakhir, untuk saat kamu siap ke langkah berikutnya.
- Block expressions — The Rust Reference (bahasa Inggris): aturan persis tentang blok dan nilai yang dihasilkannya.