Keywords: iterators, closures, borrowing, tooling
อ่านแบบคน Python:
- ถ้าอยากเอา “ภาพรวม” ก่อน: iterator chain = list comprehension +
collect()ตอนท้าย - ถ้าอยาก “ลงมือทำ”: เลือกให้ชัดว่าใช้
iter(อ่าน) /iter_mut(แก้) /into_iter(ย้าย) - ถ้าติด: เปิด 12-learning-playbook.md
บทนี้สอน iterator/closure แบบที่คนมาจาก Python จะ “ติดมือ” เร็ว: iter()/iter_mut()/into_iter(), map/filter, collect, และ move closure (จุดที่มักชนตอนเริ่มเขียน Rust)
แก่นของบทนี้:
- iterator chain = list comprehension แบบ Rust
- เลือก
iter/iter_mut/into_iterให้ตรงเจตนา (อ่าน/แก้/ย้าย) collectต้องรู้ “ปลายทางเป็นอะไร”
Python:
[f(x) for x in xs if cond(x)]Rust:
xs.iter().filter(...).map(...).collect()สังเกตความต่างเชิงนิสัย:
- Python มัก “สร้าง list ใหม่ทันที”
- Rust iterator chain มัก “ค่อย ๆ ประมวลผล” แล้ว
collect()ทีเดียวตอนสุดท้าย
สมมติ let mut v: Vec<String> = ...;
v.iter()→ ได้&String(ยืม, อ่าน)v.iter_mut()→ ได้&mut String(ยืมแบบแก้ได้)v.into_iter()→ ได้String(ย้าย ownership ออกมา)
แนวทาง (เลือกให้ตรงเจตนา):
- อ่านอย่างเดียว →
iter() - แก้ element ในที่เดิม →
iter_mut() - consume vector แล้วแปลงเป็นอย่างอื่น →
into_iter()
ตัวอย่าง: เลือกคำที่ยาว > 3 แล้วทำเป็นตัวพิมพ์ใหญ่
Python:
words = ["hi", "rust", "from", "python"]
out = [w.upper() for w in words if len(w) > 3]
print(out)Output (example):
['RUST', 'FROM', 'PYTHON']
Rust:
fn main() {
let words = vec!["hi", "rust", "from", "python"];
let out: Vec<String> = words
.iter()
.filter(|w| w.len() > 3)
.map(|w| w.to_uppercase())
.collect();
println!("{:?}", out);
}Output (example):
["RUST", "FROM", "PYTHON"]
ถ้า words: Vec<&str> แล้วคุณเรียก .iter() คุณจะได้ &&str
ช่วงแรกถ้ารู้สึกว่ามันงง ใช้ pattern unpack ใน closure ได้:
let out: Vec<String> = words
.iter()
.filter(|&&w| w.len() > 3)
.map(|&w| w.to_uppercase())
.collect();Output (example):
(no output — expression snippet only)
พอคุ้นแล้ว คุณจะเริ่มชินกับ deref coercion และไม่ต้องเขียน |&w| บ่อย
Rust ไม่ได้บังคับให้ chain เสมอไป
let mut out = Vec::new();
for w in words.iter() {
if w.len() > 3 {
out.push(w.to_uppercase());
}
}Output (example):
(no output — snippet only)
แนวทาง:
- ถ้ากำลังเรียน ให้ใช้แบบที่อ่านง่ายที่สุดก่อน
- พอ logic ชัดแล้วค่อย refactor เป็น iterator chain เพื่อให้สั้น/ชัด
Python closure มัก “จับ reference” แบบ implicit
Rust closure จะ capture ตามที่จำเป็น:
- ยืมแบบ
& - ยืมแบบ
&mut - ย้าย ownership (
move)
ตัวอย่าง: เก็บ prefix ไว้ใช้ใน map (ไม่ต้อง move)
let prefix = String::from("item:");
let out: Vec<String> = words
.iter()
.map(|w| format!("{}{}", &prefix, w))
.collect();Output (example):
(no output — snippet only)
ถ้าจะส่งไป thread/spawn มักต้อง move:
let prefix = String::from("item:");
let handle = std::thread::spawn(move || {
println!("{}", prefix);
});
handle.join().unwrap();Output (example):
item:
จำง่าย:
move= ย้าย ownership เข้า closure เพื่อให้ closure อยู่ได้โดยไม่พึ่ง scope เดิม
บางครั้ง Rust เดา type ไม่ได้ ต้องช่วยบอก:
let nums = vec!["1", "2", "3"];
let out: Vec<i32> = nums.iter().map(|s| s.parse::<i32>().unwrap()).collect();Output (example):
(no output — snippet only)
ถ้าไม่อยากใช้ unwrap() ให้ดูบท 03-error-handling.md แล้วลองทำเป็น:
Result<Vec<i32>, _>
ทิปที่คนมาจาก Python ควรลองสักครั้ง:
let out: Result<Vec<i32>, _> = nums.iter().map(|s| s.parse::<i32>()).collect();Output (example):
(no output — snippet only)
- เขียน
fn even_squares(nums: &[i32]) -> Vec<i32>
- เลือกเลขคู่ แล้วยกกำลังสอง
- เขียน
fn parse_all(nums: &[&str]) -> Result<Vec<i32>, std::num::ParseIntError>
- ใช้
map+collect::<Result<Vec<_>, _>>()
-
ทำ
Vec<String>แล้วใช้iter_mut()เพื่อเติม suffix ให้ทุกตัว เช่น"!" -
(ท้าทาย) ทำ
HashMap<String, i32>แล้วใช้ iterator เพื่อคัดเฉพาะค่ามากกว่า 10 ออกมาเป็นVec<String>ของ key