BBuzzard

Drop::drop runs when a value goes out of scope. String, Vec, File, and MutexGuard use it to free memory, close handles, and unlock. Locals drop in reverse declaration order; a struct's fields drop after its own drop, in declaration order. std::mem::drop (just drop) ends a value early by moving it into a function that does nothing.

Deref lets a type act like a reference to something inside it. String derefs to str, Vec<T> to [T], Box<T> to T. Method calls follow Deref automatically, and &String coerces to &str where a &str is expected (deref coercion). DerefMut is the mutable side.

ManuallyDrop<T> switches Drop off for one value. It is a tool for unsafe code and custom containers, not for everyday use.

Scope ends, early drop, and order

struct Guard(&'static str);

impl Drop for Guard {
    fn drop(&mut self) {
        println!("  drop {}", self.0);
    }
}

fn demo_drop_order() {
    println!("-- drop order --");

    let a = Guard("a");
    {
        let _b = Guard("b");
        println!("  inner scope ends");
    }

    let _c = Guard("c");
    drop(a);
    println!("  after drop(a)");

    // a.0; // error[E0382]: use of moved value: `a`
    // a.drop(); // error[E0040]: explicit use of destructor method
}

struct fields, Vec elements, and temporaries

struct Outer {
    _first: Guard,
    _second: Guard,
}

impl Drop for Outer {
    fn drop(&mut self) {
        println!("  drop Outer (fields drop after this)");
    }
}

fn demo_field_order() {
    println!("-- fields and elements --");

    {
        let _o = Outer { _first: Guard("field 1"), _second: Guard("field 2") };
    }

    {
        let _v = vec![Guard("vec[0]"), Guard("vec[1]")];
        println!("  vec going out of scope");
    }

    // let _ = binds nothing, so the value drops immediately.
    let _ = Guard("temporary");
    let _kept = Guard("kept until end");
    println!("  end of demo");
}

A drop guard that runs cleanup on every exit path

struct Timer {
    label: &'static str,
    start: std::time::Instant,
}

impl Drop for Timer {
    fn drop(&mut self) {
        println!("  {} cleanup ran after {:?}", self.label, self.start.elapsed());
    }
}

fn work(fail: bool) -> Result<(), &'static str> {
    let _t = Timer {
        label: if fail { "failing work" } else { "work" },
        start: std::time::Instant::now(),
    };

    if fail {
        return Err("early return still runs Drop");
    }

    Ok(())
}

Deref in std, and deref coercion

fn demo_drop_guard() {
    println!("-- drop guard --");

    println!("  {:?}", work(false));
    println!("  {:?}", work(true));
}

fn needs_str(s: &str) -> usize {
    s.len()
}

fn sum(s: &[i32]) -> i32 {
    s.iter().sum()
}

fn demo_std_deref() {
    println!("-- std Deref --");

    let s = String::from("hello");
    println!("needs_str(&String) {}", needs_str(&s));

    let r: &str = s.deref();
    let also: &str = &s;
    println!("{r} {also}");

    let v = vec![1, 2, 3];
    println!("sum(&Vec) {}", sum(&v));

    let b = Box::new(5);
    println!("*b + 1 = {}", *b + 1);

    // Coercion chains: &Box<String> -> &String -> &str.
    let bs = Box::new(String::from("boxed"));
    println!("chained {}", needs_str(&bs));
}

Implementing Deref and DerefMut

struct Stack {
    items: Vec<i32>,
    pushes: usize,
}

impl Deref for Stack {
    type Target = Vec<i32>;
    fn deref(&self) -> &Vec<i32> {
        &self.items
    }
}

impl DerefMut for Stack {
    fn deref_mut(&mut self) -> &mut Vec<i32> {
        self.pushes += 1;
        &mut self.items
    }
}

fn demo_custom_deref() {
    println!("-- custom Deref --");

    let mut st = Stack { items: vec![], pushes: 0 };
    st.push(1); // DerefMut, then Vec::push
    st.push(2);
    println!("len {} last {:?} contains 1? {}", st.len(), st.last(), st.contains(&1));
    println!("mutable derefs {}", st.pushes);

    // Use Deref for smart-pointer-like wrappers. For a newtype that should hide its
    // inner type, prefer explicit methods instead.
}

ManuallyDrop

fn demo_manually_drop() {
    println!("-- ManuallyDrop --");

    let quiet = ManuallyDrop::new(Guard("never dropped automatically"));
    println!("  holds {}", quiet.0);

    let inner: Guard = ManuallyDrop::into_inner(ManuallyDrop::new(Guard("taken back")));
    println!("  got {}", inner.0);

    // `quiet` is leaked at the end of scope; `inner` is dropped normally.
    // std::mem::forget(x) does the same as never dropping x.
    let s = ManuallyDrop::new(String::from("keep"));
    println!("  derefs to inner: {}", s.len());

    let _ = ManuallyDrop::into_inner(s);
}
$ cargo run --bin 23_drop_deref
-- drop order --
  inner scope ends
  drop b
  drop a
  after drop(a)
  drop c
-- fields and elements --
  drop Outer (fields drop after this)
  drop field 1
  drop field 2
  vec going out of scope
  drop vec[0]
  drop vec[1]
  drop temporary
  end of demo
  drop kept until end
-- drop guard --
  work cleanup ran after 110ns
  Ok(())
  failing work cleanup ran after 21ns
  Err("early return still runs Drop")
-- std Deref --
needs_str(&String) 5
hello hello
sum(&Vec) 6
*b + 1 = 6
chained 5
-- custom Deref --
len 2 last Some(2) contains 1? true
mutable derefs 2
-- ManuallyDrop --
  holds never dropped automatically
  got taken back
  derefs to inner: 4
  drop taken back

Full program

23_drop_deref/main.rs · 197 lines
View on GitHub
// 23. Drop, Deref, and ManuallyDrop
//
// Drop::drop runs when a value goes out of scope. String, Vec, File, and MutexGuard use
// it to free memory, close handles, and unlock. Locals drop in reverse declaration
// order; a struct's fields drop after its own drop, in declaration order. std::mem::drop
// (just drop) ends a value early by moving it into a function that does nothing.
//
// Deref lets a type act like a reference to something inside it. String derefs to str,
// Vec<T> to [T], Box<T> to T. Method calls follow Deref automatically, and &String
// coerces to &str where a &str is expected (deref coercion). DerefMut is the mutable
// side.
//
// ManuallyDrop<T> switches Drop off for one value. It is a tool for unsafe code and
// custom containers, not for everyday use.
//
// Run: cargo run --bin 23_drop_deref

use std::mem::ManuallyDrop;
use std::ops::{Deref, DerefMut};

struct Guard(&'static str);

impl Drop for Guard {
    fn drop(&mut self) {
        println!("  drop {}", self.0);
    }
}

// Example 1: scope ends, early drop, and order.
fn demo_drop_order() {
    println!("-- drop order --");

    let a = Guard("a");
    {
        let _b = Guard("b");
        println!("  inner scope ends");
    }

    let _c = Guard("c");
    drop(a);
    println!("  after drop(a)");

    // a.0; // error[E0382]: use of moved value: `a`
    // a.drop(); // error[E0040]: explicit use of destructor method
}

struct Outer {
    _first: Guard,
    _second: Guard,
}

impl Drop for Outer {
    fn drop(&mut self) {
        println!("  drop Outer (fields drop after this)");
    }
}

// Example 2: struct fields, Vec elements, and temporaries.
fn demo_field_order() {
    println!("-- fields and elements --");

    {
        let _o = Outer { _first: Guard("field 1"), _second: Guard("field 2") };
    }

    {
        let _v = vec![Guard("vec[0]"), Guard("vec[1]")];
        println!("  vec going out of scope");
    }

    // let _ = binds nothing, so the value drops immediately.
    let _ = Guard("temporary");
    let _kept = Guard("kept until end");
    println!("  end of demo");
}

// Example 3: a drop guard that runs cleanup on every exit path.
struct Timer {
    label: &'static str,
    start: std::time::Instant,
}

impl Drop for Timer {
    fn drop(&mut self) {
        println!("  {} cleanup ran after {:?}", self.label, self.start.elapsed());
    }
}

fn work(fail: bool) -> Result<(), &'static str> {
    let _t = Timer {
        label: if fail { "failing work" } else { "work" },
        start: std::time::Instant::now(),
    };

    if fail {
        return Err("early return still runs Drop");
    }

    Ok(())
}

fn demo_drop_guard() {
    println!("-- drop guard --");

    println!("  {:?}", work(false));
    println!("  {:?}", work(true));
}

fn needs_str(s: &str) -> usize {
    s.len()
}

fn sum(s: &[i32]) -> i32 {
    s.iter().sum()
}

// Example 4: Deref in std, and deref coercion.
fn demo_std_deref() {
    println!("-- std Deref --");

    let s = String::from("hello");
    println!("needs_str(&String) {}", needs_str(&s));

    let r: &str = s.deref();
    let also: &str = &s;
    println!("{r} {also}");

    let v = vec![1, 2, 3];
    println!("sum(&Vec) {}", sum(&v));

    let b = Box::new(5);
    println!("*b + 1 = {}", *b + 1);

    // Coercion chains: &Box<String> -> &String -> &str.
    let bs = Box::new(String::from("boxed"));
    println!("chained {}", needs_str(&bs));
}

struct Stack {
    items: Vec<i32>,
    pushes: usize,
}

impl Deref for Stack {
    type Target = Vec<i32>;
    fn deref(&self) -> &Vec<i32> {
        &self.items
    }
}

impl DerefMut for Stack {
    fn deref_mut(&mut self) -> &mut Vec<i32> {
        self.pushes += 1;
        &mut self.items
    }
}

// Example 5: implementing Deref and DerefMut.
fn demo_custom_deref() {
    println!("-- custom Deref --");

    let mut st = Stack { items: vec![], pushes: 0 };
    st.push(1); // DerefMut, then Vec::push
    st.push(2);
    println!("len {} last {:?} contains 1? {}", st.len(), st.last(), st.contains(&1));
    println!("mutable derefs {}", st.pushes);

    // Use Deref for smart-pointer-like wrappers. For a newtype that should hide its
    // inner type, prefer explicit methods instead.
}

// Example 6: ManuallyDrop.
fn demo_manually_drop() {
    println!("-- ManuallyDrop --");

    let quiet = ManuallyDrop::new(Guard("never dropped automatically"));
    println!("  holds {}", quiet.0);

    let inner: Guard = ManuallyDrop::into_inner(ManuallyDrop::new(Guard("taken back")));
    println!("  got {}", inner.0);

    // `quiet` is leaked at the end of scope; `inner` is dropped normally.
    // std::mem::forget(x) does the same as never dropping x.
    let s = ManuallyDrop::new(String::from("keep"));
    println!("  derefs to inner: {}", s.len());

    let _ = ManuallyDrop::into_inner(s);
}

fn main() {
    demo_drop_order();
    demo_field_order();
    demo_drop_guard();
    demo_std_deref();
    demo_custom_deref();
    demo_manually_drop();
}