Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

รันโค้ดตอน 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

Filename: src/main.rs
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");
}
Listing 15-14: struct 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

Filename: src/main.rs
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");
}
Listing 15-15: พยายามเรียกเมธอด 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 ได้

Filename: src/main.rs
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");
}
Listing 15-16: เรียก 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