Closures
Versi Bahasa Indonesia. Baca dalam Bahasa Inggris
Closure adalah konstruksi mirip fungsi yang dapat menangkap variabel dari lingkungan sekitarnya. Closure bersifat ringan dan fleksibel, memungkinkan Anda menulis kode yang ringkas yang dapat mengenkapsulasi konteks dari tempat ia didefinisikan.
Sintaks dasar closure di Rust adalah:
|parameters| expression_or_block- Anonymous: Closure tidak memiliki nama dan sering ditulis secara inline.
- Environment Capturing: Closure dapat menangkap nilai dari scope di mana ia didefinisikan.
- Traits:
Fn: Digunakan untuk closure yang tidak memodifikasi variabel yang ditangkap.FnMut: Digunakan untuk closure yang memodifikasi variabel yang ditangkap.FnOnce: Digunakan untuk closure yang mengambil kepemilikan (ownership) variabel yang ditangkap.
Contoh
Closure Dasar
fn main() {
let add = |x, y| x + y; // Closure yang menjumlahkan dua angka
println!("Sum: {}", add(1, 2)); // 3
}Menangkap Environment
Closure secara inheren fleksibel dan akan melakukan apa yang diperlukan agar closure berfungsi tanpa anotasi. Ini memungkinkan penangkapan untuk secara fleksibel beradaptasi dengan use case, terkadang memindahkan (moving) dan terkadang meminjam (borrowing). Closure dapat menangkap variabel:
- by reference:
&T - by mutable reference:
&mut T - by value:
T
By Reference
fn main() {
let color = String::from("green");
// Sebuah closure untuk mencetak `color` yang segera meminjam (`&`) `color` dan
// menyimpan pinjaman dan closure dalam variabel `print`. `color` akan tetap
// dipinjam sampai `print` digunakan untuk terakhir kalinya.
//
// `println!` hanya membutuhkan argumen berupa immutable reference sehingga tidak
// memaksakan sesuatu yang lebih restriktif.
let print = || println!("`color`: {}", color);
// Panggil closure menggunakan pinjaman.
print();
// `color` bisa dipinjam secara immutably lagi, karena closure hanya menyimpan
// immutable reference ke `color`.
let _reborrow = &color;
print();
// Move atau reborrow diizinkan setelah penggunaan terakhir `print`
let _color_moved = color;
}By Mutable Reference
fn main() {
let mut count = 0;
// Sebuah closure untuk menaikkan `count` bisa mengambil `&mut count` atau `count`
// tetapi `&mut count` lebih tidak restriktif sehingga ia mengambil itu. Segera
// meminjam `count`.
//
// `mut` diperlukan pada `inc` karena `&mut` disimpan di dalamnya. Dengan demikian,
// memanggil closure memutasikan `count` yang memerlukan `mut`.
let mut inc = || {
count += 1;
println!("`count`: {}", count);
};
// Panggil closure menggunakan mutable borrow.
inc();
// Closure masih meminjam `count` secara mutable karena akan dipanggil nanti.
// Percobaan untuk reborrow akan menyebabkan error.
// let _reborrow = &count;
// ^ TODO: coba uncomment baris ini.
inc();
// Closure tidak lagi perlu meminjam `&mut count`. Oleh karena itu,
// reborrow dapat dilakukan tanpa error.
let _count_reborrowed = &mut count;
}-
Jika kita uncomment baris
let _reborrow = &count;kita akan mendapatkan compile error berikut karena kita sudah meminjamnya di panggilan closure di bawahnya.error[E0502]: cannot borrow `count` as immutable because it is also borrowed as mutable -
Tetapi jika kita menghapus panggilan kedua ke closure, itu akan berfungsi dengan baik karena kita sudah selesai dengan closure sehingga
counttidak lagi dipinjam.
By Value
fn main() {
use std::mem;
// Sebuah non-copy type.
let movable = Box::new(3);
// `mem::drop` membutuhkan `T` sehingga ini harus mengambil by value. Copy type
// akan menyalin ke dalam closure, meninggalkan yang asli tidak tersentuh.
// Non-copy harus berpindah dan `movable` segera pindah ke dalam closure.
let consume = || {
println!("`movable`: {:?}", movable);
mem::drop(movable);
};
// `consume` menghabiskan variabel sehingga ini hanya bisa dipanggil sekali.
consume();
// consume();
// ^ TODO: Coba uncomment baris ini.
}-
Jika kita uncomment panggilan kedua ke closure
consume(), kita akan mendapatkan compile error di bawah karena kita memindahkan/men-drop variabelmovable.error[E0382]: use of moved value: `consume` -
Tetapi jika kita tidak memindahkan/men-drop variabel
movable, itu akan berfungsi dengan baik.
Closure Traits
Fn
fn call_fn<F: Fn()>(f: F) {
f();
f(); // bisa dipanggil berkali-kali
}
fn main() {
let x = 10;
let print_x = || println!("x is {}", x);
call_fn(print_x);
}- Menangkap variabel by reference.
- Dapat dipanggil berkali-kali.
- Karena
FnMutdanFnOnceadalah supertraits dariFn, instanceFnapa pun dapat digunakan sebagai parameter di manaFnMutatauFnOncediharapkan.
FnMut
fn call_fn_mut<F: FnMut()>(mut f: F) {
f();
f(); // bisa dipanggil berkali-kali
}
fn main() {
let mut x = 10;
let mut modify_x = || x += 1; // Menangkap `x` by mutable reference
call_fn_mut(modify_x);
println!("x is {}", x); // 12
}- Menangkap variabel by mutable reference.
- Dapat memodifikasi variabel yang ditangkap.
- Karena
FnOnceadalah supertrait dariFnMut, instanceFnMutapa pun dapat digunakan di manaFnOncediharapkan, dan karenaFnadalah subtrait dariFnMut, instanceFnapa pun dapat digunakan di manaFnMutdiharapkan.
FnOnce
fn call_fn_once<F: FnOnce()>(f: F) {
f();
// Panggilan di bawah akan menghasilkan error: closure `FnOnce` hanya bisa dipanggil sekali
// f();
}
fn main() {
let x = String::from("Hello");
let consume_x = || println!("{}", x); // Menangkap `x` by value
call_fn_once(consume_x);
}- Menghabiskan variabel yang ditangkap by value.
- Hanya bisa dipanggil sekali.
- Karena
FndanFnMutadalah subtraits dariFnOnce, instanceFnatauFnMutapa pun dapat digunakan di manaFnOncediharapkan.
Best Practices dengan Closures
- Gunakan closure untuk logika pendek dan inline:
- Mereka sempurna untuk tugas seperti sorting, filtering, atau mapping pada collections.
- Pilih trait yang tepat (Fn, FnMut, FnOnce):
- Gunakan
Fnuntuk operasi read-only. - Gunakan
FnMutuntuk memodifikasi state. - Gunakan
FnOnceuntuk menghabiskan (consume) variabel yang ditangkap.
- Gunakan
- Manfaatkan type inference:
- Hindari menentukan tipe parameter kecuali diperlukan untuk kejelasan atau disambiguasi.
- Minimalkan environment capture:
- Perhatikan bagaimana closure menangkap variabel, terutama dalam kode yang sensitif terhadap performa.
Referensi
- https://doc.rust-lang.org/book/ch13-01-closures.html
- https://doc.rust-lang.org/rust-by-example/fn/closures/capture.html
- https://doc.rust-lang.org/std/ops/trait.FnOnce.html
- https://doc.rust-lang.org/std/ops/trait.Fn.html
- https://doc.rust-lang.org/std/ops/trait.FnMut.html
Last updated on