สรุป The Rust Programming Language ฉบับมือเก่า ตอนที่ 8
บทความนี้สรุปจากหนังสือ The Rust Programming Language chapter 8 ด้วยความคิดเห็นของผมเอง อาจมีการตีความที่มีโอกาสผิดพลาดได้ ทางที่ดีถ้าอยากได้ reference จริงๆแนะนำต้นทางครับ
บทนี้จะพูดถึง collections 3 ตัวคือ Vector, Strings และ Hash Maps
Vector
Vector
ถ้าให้อธิบายสั้นๆก็คงเป็น array ที่อยู่ใน heap แทนที่จะเป็น stack แล้ว powerful กว่าเพราะมาพร้อม feature ที่ครบเครื่องกว่า เช่น size สามารถเพิ่มลดได้ เป็นต้น (แต่ยังคงต้องเป็น collection of the same type เหมือนกัน)
มีประเด็นนิดหน่อยตรงการอ่านค่า
- indexing เช่น
arr[10]
แบบนี้สามารถ panic ได้ถ้า out of bound - ใช้
arr.get(10)
แบบนี้จะไม่ panic แต่จะ returnOption<&T>
มาแทน
มีอีกเรื่องนึงคือถึงจะบอกว่าทั้ง collection ต้องเก็บ type เดียวกัน แต่เราสามารถใช้ enum ที่มี variants หลายๆ types ได้ อารมณ์เหมือน vec<UnionType>
โดยที่เจ้า UnionType ในทีนี้คือ enum
Strings
บทความนี้ตอนแรกผมคิดว่าจะไม่มีอะไรเลย แต่ก็กลับมีสิ่งที่น่าสนใจอยู่
- behind the scenes at the core ทุก string คือ slices ที่เป็น fixed length UTF-8 encoded
- Strings จริงๆข้างหลังก็คือ Vector ของ bytes ที่ wrap ตัว slices แล้วทำให้มัน growable, mutable, owned
- ที่ต่างกับ Vector ทั่วๆไปคือไม่สามารถใช้ indexing ได้ (คือไม่สามารถ
s[10]
แบบนี้ได้) เหตุผลคือพอเป็น UTF-8 encoded แล้วบางทีจะมีตัวอักษรที่ต้องใช้ 2 bytes ในการ represent แต่ Rust ไม่ได้รู้ว่า user ต้องการให้ return byte หรือ character จาก indexing - ก็เลยต้องใช้ range expression มาช่วยบอกให้ชัดๆ เช่น
&s[0..4]
แบบนี้ก็จะได้ค่าไป 4 bytes ซึ่งถ้าเป็นภาษาที่ตัวอักษรนึงคือ 2 bytes ก็จะได้ไป 2 characters อะไรแบบนี้ - แล้วถ้าเกิดเราใช้
&s[0..1]
เพื่อให้ได้ effect เดียวกับ indexing&s[0]
ในภาษาที่เป็นเป็น 2-byte จะเกิออะไรขึ้น คำตอบก็คือจะโดน panic ที่ runtime - ทางที่ดีใช้
.chars()
หรือ.bytes()
แล้ว iterate เอาเลย safe สุด
for c in "Зд".chars() {
println!("{c}");
}
for b in "Зд".bytes() {
println!("{b}");
}
Hash Maps
อันนี้โดย basic ทั่วๆไปก็คล้ายๆกับ implementation ในภาษาอื่นๆนั่นแหล่ะ แต่มีความน่าสนใจนิดหน่อย
map.get()
จะ returnOption<&Value>
ซึ่งเป็น immutable reference ถ้าไม่อยากให้มี reference ของ value อยู่ใน scope ก็ให้ใช้.copies()
จะได้Option<Value>
แทน (เช่นmap.get(&key).copies()
)- Iterate over ง่ายๆ ด้วย
for (key, value) in &map
- Function insert takes ownership นะ เช่น
map.insert(key, value);
แบบนี้ตัวแปรkey
กับvalue
จะโดน take ownership ไปแล้วจะไม่ valid ใน scope หลังจากนั้นอีกแล้ว - Insert ด้วย key ซ้ำก็ overwrite ปกติตาม common sense
- สามารถทำการ
insert if not exist
ได้ด้วยmap.entry(key).or_insert(value)
upsert
ก็ทำได้คล้ายๆกัน
let count = map.entry(key).or_insert(0);
*count += 1;
อย่าง code ข้างต้นก็คือจะ default value เป็น 0 หรือใช้ value ที่มีอยู่แล้ว จากนั้น dereference
มาเอาค่ามา update (dereference ผมขอรอให้หนังสืออธิบายในบทต่อๆไปละกัน)
เย้จบแล้วบทที่ 8