ข้ามไปยังเนื้อหา

Closures

Closure คือ Function นิรนามที่กำหนด Inline ด้วย Syntax |params| body ต่างจาก fn ทั่วไป Closure สามารถ จับ (Capture) ตัวแปรจาก Scope ที่ล้อมรอบได้

let multiplier = 3;
let triple = |x| x * multiplier; // จับ multiplier จาก Environment
println!("{}", triple(7)); // 21

Rust มี Trait สำหรับ Closure สามแบบที่อธิบายว่า Closure ใช้ค่าที่จับไว้อย่างไร:

Traitวิธีการจับสามารถเรียกได้
Fnยืมแบบ Immutable (&T)กี่ครั้งก็ได้
FnMutยืมแบบ Mutable (&mut T)กี่ครั้งก็ได้
FnOnceรับ Ownership (T)ครั้งเดียวเท่านั้น

ทุก Closure Implement FnOnce เป็นอย่างน้อย ถ้า Closure แค่ยืมค่า ก็จะ Implement Fn ด้วย

fn apply<F: Fn(i32) -> i32>(f: F, x: i32) -> i32 { f(x) }
let base = 10;
let add_base = |x| x + base; // ยืม base แบบ Immutable
println!("{}", apply(add_base, 5)); // 15
let mut count = 0;
let mut increment = || { count += 1; count };
println!("{}", increment()); // 1
println!("{}", increment()); // 2
drop(increment); // ปล่อย Mutable Borrow ก่อนอ่าน count
println!("count is {}", count); // 2

ต้องเรียก drop(increment) ก่อนอ่าน count โดยตรง เพราะ increment ถือ &mut count ที่จะขัดแย้งกับ Immutable Borrow ใน println!

let name = String::from("Rust");
let greet = || println!("Hello, {}!", name); // ย้าย name เข้า Closure
greet(); // name ถูก Consume ที่นี่
// greet(); // Compile Error — FnOnce เรียกได้แค่ครั้งเดียว

move บังคับให้ Closure รับ Ownership ของตัวแปรที่จับทั้งหมด ไม่ว่าจะยืมเพียงอย่างเดียวก็ตาม:

let message = String::from("hi");
let say = move || println!("{}", message); // message ถูกย้ายเข้า say
// println!("{}", message); // error — message ถูกย้ายไปแล้ว
say();

move จำเป็นเมื่อ Closure มีอายุยาวกว่า Scope ที่กำหนด (เช่น ส่งไปยัง Thread)

Iterator Adaptor เช่น map และ filter รับ Closure โดยตรง:

let threshold = 3;
let big: Vec<i32> = vec![1, 2, 3, 4, 5]
.iter()
.filter(|&&x| x > threshold) // Closure จับ threshold ด้วย &
.map(|&x| x * 10)
.collect();
// [40, 50]
fn apply<F: Fn(i32) -> i32>(f: F, x: i32) -> i32 {
f(x)
}
fn apply_mut<F: FnMut() -> i32>(mut f: F) -> i32 {
f() + f()
}
fn apply_once<F: FnOnce() -> String>(f: F) -> String {
f()
}
fn main() {
// Fn: borrows environment immutably
let multiplier = 3;
let triple = |x| x * multiplier;
println!("apply triple to 5: {}", apply(triple, 5));
// FnMut: captures and mutates
let mut count = 0;
let mut increment = || { count += 1; count };
let result = apply_mut(&mut increment);
drop(increment); // release mutable borrow before reading count
println!("FnMut result: {}, count is now: {}", result, count);
// FnOnce + move: takes ownership
let name = String::from("Rust");
let greeting = move || format!("Hello, {}!", name);
println!("{}", apply_once(greeting));
// closures as iterator adaptors
let nums = vec![1, 2, 3, 4, 5];
let threshold = 3;
let big: Vec<i32> = nums
.iter()
.filter(|&&x| x > threshold)
.map(|&x| x * 10)
.collect();
println!("big: {:?}", big);
}
Closure Trait ใดอธิบาย Closure ที่ยืมตัวแปรที่จับแบบ Mutable?
ทำไมต้องเรียก drop(increment) ก่อน println!("{}", count) ในตัวอย่าง FnMut?
Keyword move ทำอะไรกับ Closure?
Closure ที่แค่อ่านตัวแปรที่จับโดยไม่แก้ไข Implement Trait ใดบ้าง?