Rust: ว่าด้วยเรื่อง Ownership/Scope/Drop
ภาษา Computer สมัยใหม่ ส่วนใหญ่ เลือกจะจัดการหน่วยความจำที่ไม่ใช้แล้วด้วยการเพิ่มระบบ GC (Garbage Collection) เข้าไปในระบบ runtime ของภาษา
GC มีหน้าที่ตรวจสอบ ไล่ดูว่ามีหน่วยความจำส่วนไหนที่จองไว้ แล้วไม่ได้ใช้งานอีกบ้าง คือไม่มีตัวแปรไหนอ้างอิงหน่วยความจำนี้อีกแล้ว GC ก็จะกวาดคืนระบบ (Operating System) ไป หรือบางครั้งก็ไม่คืน แต่มาร์คไว้ว่า เอาไปทำอย่างอื่นต่อได้แล้ว
Rust เป็นภาษาสมัยใหม่เช่นกัน แต่ เลือกวิธีการในการคืนหน่วยความจำที่ไม่ใช้แล้วโดยไม่ใช้ GC แต่ใช้กลไกในเรื่อง Ownership และความมีชีวิตอยู่ของตัวแปรใน Scope แล้ว Drop เพื่อคืนหน่วยความจำ
Ownership
ใน Rust เมื่อเราสร้างค่าบางอย่างขึ้นมาแล้วกำหนดให้ตัวแปร จะเกิดการจองหน่วยความจำเพื่อใช้เก็บค่านี้และกำหนดให้ตัวแปร ตัวแปรนั้นๆจะมีความเป็นเจ้าของ (owner) พื้นที่หน่วยความจำนี้อยู่ เช่น
fn main() {
let msg = String::from("Hello, World"); println!("{}", msg);
}
(* เหตุผลที่เลือกใช้ String::from
ในการสร้างค่าของ type String
เนื่องจากค่าของ type นี้ใช้หน่วยความจำในส่วนของ Heap ที่จำเป็นต้อง Drop ซึ่งเราจะได้อธิบายเรื่อง Drop อีกครั้ง *)
msg เป็นเจ้าของค่าของ type String ที่เราสร้าง ซึ่งเกิดการจองหน่วยความจำ Heap เกิดขึ้น
Scope
Scope คือขอบเขตที่เรายังสามารถเรียกใช้ตัวแปรนั้นๆได้ ซึ่งสำหรับ Rust คือบล็อคของ {} คือจุดกำหนดขอบเขต ตัวแปรที่สร้างภายใต้ {} จะมีชีวิตอยู่เพื่อใช้งานได้ตั้งแต่ปีกกาเปิด จนจบปีกกาปิด ซึ่งเราสามารถสร้าง {} ซ้อนใน {} ของ function ได้เช่น
fn main() {
{
let msg = String::from("Hello, World"); println!("{}", msg);
}
}
ซึ่งถ้า ตัวแปรประกาศอยู่ใน {} ด้านในแล้วเราไม่สามารถใช้ด้านนอกหลังจากจบ scope ได้เช่น แบบนี้จะเกิด error ขึ้น
fn main() {
{
let msg = String::from("Hello, World");
} println!("{}", msg);
}Compiling playground v0.0.1 (/playground)
error[E0425]: cannot find value `msg` in this scope
--> src/main.rs:5:20
|
5 | println!("{}", msg);
| ^^^ not found in this scope
error: aborting due to previous error
For more information about this error, try `rustc --explain E0425`.
error: Could not compile `playground`.
To learn more, run the command again with --verbose.
Drop
ในภาษาอื่นที่มี GC เมื่อจบ scope แบบข้างบน หน่วยความจำที่จองไว้สำหรับ msg
เมื่อถึงช่วงเวลาที่ GC ทำงานจะ scan จนพบว่าไม่มีใครใช้หน่วยความจำนี้แล้วเพราะ msg จบ scope ไปแล้ว ก็จะเรียกคืนหน่วยความจำให้
แต่ Rust ไม่มี GC กลไกจะต่างกันนิดนึงคือเมื่อจบ scope Rust จะเรียก method drop
ของ type เช่นเคสนี้คือ drop
ของ type String
เพื่อคืนหน่วยความจำที่จองมาสำหรับ msg
นี่คือกลไกที่ Rust ใช้งาน ไม่จำเป็นต้องรอ GC มาเรียกเก็บ แต่จะเรียก drop
ทันทีเมื่อจบ scope
Rust ยังมีกลไกในเรื่อง Move ownership และ Borrow by reference อีกที่ช่วยให้ Rust นั้นตรวจสอบการมีชีวิตอยู่ของตัวแปรและหน่วยความจำที่จองไว้ เพื่อทำให้ drop ได้อย่างถูกต้อง ซึ่ง Rust ตรวจสอบความถูกต้องพวกนี้ได้ตั้งแต่ตอน compile เดี๋ยวจะสรุปอีกทีวันหลังครับ