รันโค้ดตอน Cleanup ด้วย Trait Drop
trait ที่สองที่สำคัญต่อ pattern smart pointer คือ Drop ซึ่งให้คุณ
กำหนดสิ่งที่เกิดเมื่อค่ากำลังจะออกจาก scope คุณให้ implementation
สำหรับ trait Drop บน type ใดก็ได้ และโค้ดนั้นใช้ปล่อย resource
เช่นไฟล์หรือ network connection ได้
เรากำลังแนะนำ Drop ใน context ของ smart pointer เพราะ functionality
ของ trait Drop เกือบเสมอถูกใช้เมื่อ implement smart pointer
ตัวอย่างเช่น เมื่อ Box<T> ถูก drop มันจะ deallocate พื้นที่บน heap
ที่ box ชี้
ในบางภาษา สำหรับบาง type programmer ต้องเรียกโค้ดเพื่อปล่อย memory หรือ resource ทุกครั้งที่พวกเขาเสร็จใช้ instance ของ type เหล่านั้น ตัวอย่างรวม file handle, socket และ lock ถ้า programmer ลืม ระบบอาจ overload และ crash ใน Rust คุณระบุได้ว่าโค้ดเฉพาะถูกรันเมื่อใดก็ตาม ที่ค่าออกจาก scope และ compiler จะ insert โค้ดนี้อัตโนมัติ ผลคือ คุณไม่ต้องระวังเกี่ยวกับการวางโค้ด cleanup ทุกที่ในโปรแกรมที่ instance ของ type เฉพาะเสร็จ — คุณยังจะไม่รั่ว resource!
คุณระบุโค้ดที่จะรันเมื่อค่าออกจาก scope โดย implement trait Drop
trait Drop ต้องการให้คุณ implement หนึ่งเมธอดชื่อ drop ที่รับ
mutable reference ของ self เพื่อเห็นเมื่อ Rust เรียก drop มา
implement drop ด้วย statement println! ตอนนี้
Listing 15-14 แสดง struct CustomSmartPointer ที่ functionality
custom เดียวคือมันจะ print Dropping CustomSmartPointer! เมื่อ
instance ออกจาก scope เพื่อแสดงเมื่อ Rust รันเมธอด drop
struct CustomSmartPointer {
data: String,
}
impl Drop for CustomSmartPointer {
fn drop(&mut self) {
println!("Dropping CustomSmartPointer with data `{}`!", self.data);
}
}
fn main() {
let c = CustomSmartPointer {
data: String::from("my stuff"),
};
let d = CustomSmartPointer {
data: String::from("other stuff"),
};
println!("CustomSmartPointers created");
}
CustomSmartPointer ที่ implement trait Drop ที่เราจะวางโค้ด cleanup ของเราtrait Drop ถูกรวมใน prelude ดังนั้นเราไม่ต้องนำมันเข้า scope เรา
implement trait Drop บน CustomSmartPointer และให้ implementation
สำหรับเมธอด drop ที่เรียก println! body ของเมธอด drop คือที่
คุณจะวาง logic ใดที่คุณต้องการให้รันเมื่อ instance ของ type ของคุณ
ออกจาก scope เรากำลัง print text ที่นี่เพื่อสาธิตด้วยภาพเมื่อ Rust
จะเรียก drop
ใน main เราสร้างสอง instance ของ CustomSmartPointer แล้ว print
CustomSmartPointers created ที่ท้ายสุดของ main instance ของ
CustomSmartPointer ของเราจะออกจาก scope และ Rust จะเรียกโค้ดที่
เราใส่ในเมธอด drop print ข้อความสุดท้ายของเรา สังเกตว่าเราไม่ต้อง
เรียกเมธอด drop ชัดเจน
เมื่อเรารันโปรแกรมนี้ เราจะเห็น output ต่อไปนี้:
$ cargo run
Compiling drop-example v0.1.0 (file:///projects/drop-example)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.60s
Running `target/debug/drop-example`
CustomSmartPointers created
Dropping CustomSmartPointer with data `other stuff`!
Dropping CustomSmartPointer with data `my stuff`!
Rust เรียก drop ให้เราอัตโนมัติเมื่อ instance ของเราออกจาก scope
เรียกโค้ดที่เราระบุ ตัวแปรถูก drop ในลำดับย้อนกลับของการสร้างพวกมัน
ดังนั้น d ถูก drop ก่อน c จุดประสงค์ของตัวอย่างนี้คือให้คุณเห็น
ด้วยภาพว่าเมธอด drop ทำงานยังไง — โดยปกติคุณจะระบุโค้ด
cleanup ที่ type ของคุณต้องการรันแทนข้อความ print
โชคไม่ดี ไม่ตรงไปตรงมาที่จะ disable functionality drop อัตโนมัติ
การ disable drop มักไม่จำเป็น — ประเด็นทั้งหมดของ trait Drop คือ
มันถูกดูแลอัตโนมัติ บางครั้ง อย่างไรก็ตาม คุณอาจต้องการทำความสะอาด
ค่าเร็ว ตัวอย่างหนึ่งคือเมื่อใช้ smart pointer ที่จัดการ lock — คุณ
อาจต้องการบังคับเมธอด drop ที่ปล่อย lock เพื่อให้โค้ดอื่นใน scope
เดียวกัน acquire lock ได้ Rust ไม่ให้คุณเรียกเมธอด drop ของ trait
Drop ด้วยมือ — แทน คุณต้องเรียกฟังก์ชัน std::mem::drop ที่
standard library ให้มาถ้าคุณต้องการบังคับให้ค่าถูก drop ก่อนสิ้นสุด
ของ scope ของมัน
การพยายามเรียกเมธอด drop ของ trait Drop ด้วยมือโดยแก้ฟังก์ชัน
main จาก Listing 15-14 จะไม่ทำงาน ดังที่แสดงใน Listing 15-15
struct CustomSmartPointer {
data: String,
}
impl Drop for CustomSmartPointer {
fn drop(&mut self) {
println!("Dropping CustomSmartPointer with data `{}`!", self.data);
}
}
fn main() {
let c = CustomSmartPointer {
data: String::from("some data"),
};
println!("CustomSmartPointer created");
c.drop();
println!("CustomSmartPointer dropped before the end of main");
}
drop จาก trait Drop ด้วยมือเพื่อ cleanup เร็วเมื่อเราพยายามคอมไพล์โค้ดนี้ เราจะได้ error นี้:
$ cargo run
Compiling drop-example v0.1.0 (file:///projects/drop-example)
error[E0040]: explicit use of destructor method
--> src/main.rs:16:7
|
16 | c.drop();
| ^^^^ explicit destructor calls not allowed
|
help: consider using `drop` function
|
16 - c.drop();
16 + drop(c);
|
For more information about this error, try `rustc --explain E0040`.
error: could not compile `drop-example` (bin "drop-example") due to 1 previous error
ข้อความ error นี้ระบุว่าเราไม่ได้รับอนุญาตให้เรียก drop ชัดเจน
ข้อความ error ใช้คำ destructor ซึ่งเป็นคำ programming ทั่วไป
สำหรับฟังก์ชันที่ cleanup instance destructor คล้ายกับ
constructor ซึ่งสร้าง instance ฟังก์ชัน drop ใน Rust เป็น
destructor เฉพาะหนึ่ง
Rust ไม่ให้เราเรียก drop ชัดเจน เพราะ Rust ยังจะเรียก drop
อัตโนมัติบนค่าที่ท้ายสุดของ main นี่จะทำให้เกิด error double free
เพราะ Rust จะพยายาม cleanup ค่าเดียวกันสองครั้ง
เราไม่สามารถ disable การ insert drop อัตโนมัติเมื่อค่าออกจาก scope
และเราเรียกเมธอด drop ชัดเจนไม่ได้ ดังนั้น ถ้าเราต้องการบังคับให้
ค่าถูก cleanup เร็ว เราใช้ฟังก์ชัน std::mem::drop
ฟังก์ชัน std::mem::drop ต่างจากเมธอด drop ใน trait Drop
เราเรียกมันโดยส่งค่าที่เราต้องการ force-drop เป็นอาร์กิวเมนต์
ฟังก์ชันอยู่ใน prelude ดังนั้นเราแก้ main ใน Listing 15-15 เพื่อ
เรียกฟังก์ชัน drop ดังที่แสดงใน Listing 15-16 ได้
struct CustomSmartPointer {
data: String,
}
impl Drop for CustomSmartPointer {
fn drop(&mut self) {
println!("Dropping CustomSmartPointer with data `{}`!", self.data);
}
}
fn main() {
let c = CustomSmartPointer {
data: String::from("some data"),
};
println!("CustomSmartPointer created");
drop(c);
println!("CustomSmartPointer dropped before the end of main");
}
std::mem::drop เพื่อ drop ค่าชัดเจนก่อนมันออกจาก scopeการรันโค้ดนี้จะ print ต่อไปนี้:
$ cargo run
Compiling drop-example v0.1.0 (file:///projects/drop-example)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.73s
Running `target/debug/drop-example`
CustomSmartPointer created
Dropping CustomSmartPointer with data `some data`!
CustomSmartPointer dropped before the end of main
text Dropping CustomSmartPointer with data `some data`! ถูก print
ระหว่าง text CustomSmartPointer created และ
CustomSmartPointer dropped before the end of main แสดงว่าโค้ดของ
เมธอด drop ถูกเรียกเพื่อ drop c ที่จุดนั้น
คุณใช้โค้ดที่ระบุใน implementation trait Drop ได้หลายวิธีเพื่อทำ
cleanup สะดวกและปลอดภัย — ตัวอย่างเช่น คุณใช้มันเพื่อสร้าง memory
allocator ของคุณเอง! ด้วย trait Drop และระบบ ownership ของ Rust
คุณไม่ต้องจำที่จะ cleanup เพราะ Rust ทำมันอัตโนมัติ
คุณยังไม่ต้องกังวลเกี่ยวกับปัญหาที่เกิดจากการ cleanup ค่าที่ยังใช้
อยู่โดยบังเอิญ — ระบบ ownership ที่ทำให้แน่ใจว่า reference valid
เสมอยังรับประกันว่า drop ถูกเรียกเพียงครั้งเดียวเมื่อค่าไม่ถูก
ใช้อีก
ตอนนี้เราตรวจสอบ Box<T> และลักษณะของ smart pointer บางอย่างแล้ว
มาดู smart pointer อื่นที่นิยามใน standard library