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

Closures & Iterators

ใน TypeScript arrow functions สามารถจับตัวแปรจาก enclosing scope ได้อย่างอิสระ Rust closures ทำสิ่งเดียวกัน แต่ compiler ติดตามอย่างชัดเจนว่า closure ใช้ค่าที่จับไว้อย่างไร และบังคับ ownership rules ตอน compile time

TypeScript
// TypeScript — arrow function captures 'factor' by reference
const factor = 3;
const triple = (x: number) => x * factor;
console.log(triple(5)); // 15
// Closures in higher-order functions
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(x => x * 2);
console.log(doubled); // [2, 4, 6, 8, 10]
// Capturing mutable state — counter
let count = 0;
const increment = () => { count++; };
increment();
increment();
console.log(count); // 2
Rust
fn main() {
// Closure captures 'factor' by reference (immutable)
let factor = 3;
let triple = |x| x * factor;
println!("{}", triple(5)); // 15
// Iterator adapters: map, filter, collect
let numbers = vec![1, 2, 3, 4, 5];
let doubled: Vec<i32> = numbers.iter().map(|&x| x * 2).collect();
println!("{:?}", doubled); // [2, 4, 6, 8, 10]
// FnMut closure — captures count mutably
let mut count = 0;
let mut increment = || { count += 1; };
increment();
increment();
drop(increment); // release borrow so we can use count again
println!("{count}"); // 2
}

Rust จำลองความสามารถของ closure เป็นสาม traits การเข้าใจสิ่งเหล่านี้มีความสำคัญเมื่อคุณส่ง closures ไปยังฟังก์ชันหรือเก็บไว้ใน structs

Traitทำอะไรได้คู่เทียบ TypeScript
FnOnceเรียกได้ครั้งเดียว; สามารถ move ค่าที่จับไว้ออกได้One-shot callback ที่ consume ค่าที่จับไว้
FnMutเรียกได้หลายครั้ง; เปลี่ยนแปลงค่าที่จับไว้Stateful callback ที่แก้ไขตัวแปรที่ close over
Fnเรียกได้หลายครั้ง; อ่านค่าที่จับไว้เท่านั้น (หรือไม่จับเลย)Pure arrow function ไม่มี side effects บนตัวแปรที่จับ

ทุก Fn เป็น FnMut ด้วย; ทุก FnMut เป็น FnOnce ด้วย เมื่อคุณรับ parameter &dyn Fn(T) -> U คุณยอมรับ requirement ที่เข้มงวดที่สุด — closure ต้องเรียกได้หลายครั้งโดยไม่ mutate

fn apply_twice<F: Fn(i32) -> i32>(f: F, x: i32) -> i32 {
f(f(x))
}
fn main() {
let double = |x| x * 2;
println!("{}", apply_twice(double, 3)); // 12
}

.map(), .filter(), และ .reduce() ของ TypeScript eager: จัดสรร array ใหม่ในแต่ละขั้น Iterator adapters ของ Rust lazy: ไม่มีการคำนวณจนกว่าคุณจะเรียก consuming method เช่น .collect(), .sum(), หรือ .for_each() หมายความว่า chain ยาวของ .map().filter().take() ไม่จัดสรร vectors ระหว่างกลางเลย

TypeScript
// TypeScript — each step allocates a new array
const result = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
.filter(x => x % 2 === 0) // new array [2,4,6,8,10]
.map(x => x * x) // new array [4,16,36,64,100]
.slice(0, 3); // new array [4,16,36]
console.log(result);
Rust
fn main() {
// Rust — no intermediate allocations until collect()
let result: Vec<i32> = (1..=10)
.filter(|x| x % 2 == 0) // lazy
.map(|x| x * x) // lazy
.take(3) // lazy
.collect(); // ONE allocation here
println!("{:?}", result); // [4, 16, 36]
// Common adapters
let sum: i32 = (1..=100).sum();
println!("sum 1..100 = {sum}");
let words = vec!["hello", "world", "rust"];
let upper: Vec<String> = words.iter()
.map(|w| w.to_uppercase())
.collect();
println!("{:?}", upper);
// flat_map (like Array.flatMap)
let sentences = vec!["hello world", "foo bar"];
let all_words: Vec<&str> = sentences.iter()
.flat_map(|s| s.split_whitespace())
.collect();
println!("{:?}", all_words);
}
fn main() {
let multiplier = 3;
let triple = |x| x * multiplier;
println!("triple(5) = {}", triple(5));
let numbers = vec![1, 2, 3, 4, 5, 6];
let evens_doubled: Vec<i32> = numbers
.iter()
.filter(|&&x| x % 2 == 0)
.map(|&x| x * 2)
.collect();
println!("evens_doubled = {:?}", evens_doubled);
let sum: i32 = numbers.iter().sum();
println!("sum = {sum}");
let result: Vec<String> = (1..=3)
.map(|x| x * x)
.map(|x| format!("sq:{x}"))
.collect();
println!("{:?}", result);
}
ควรใช้ `Fn` trait ใดเมื่อ closure แค่อ่าน (ไม่ mutate) ค่าที่จับไว้และสามารถเรียกได้หลายครั้ง?
Iterator chain ของ Rust คำนวณจริงเมื่อใด?
keyword `move` ทำอะไรเมื่อใช้ก่อน closure?
Iterator adapter ใดของ Rust ที่เทียบเท่ากับ `Array.prototype.flatMap` ของ JavaScript?