bigywater.tech
Rust · 55

Drop Trait 与资源释放

Drop 什么时候运行、按什么顺序运行,是写资源管理代码(锁、文件、连接)的基础。这篇给一个会打印自己名字的类型,把每种情形的 drop 顺序都真实跑出来,包括最容易误会的 `let _ =` 和 process::exit。

逆序
同一作用域里局部变量的 drop 顺序
E0040
手动调用 .drop() 的编译错误
101
panic 后进程退出码,但 guard 仍被 drop

drop 的顺序

实测 · 本机执行输出
  源码:
      1 | struct D(&'static str);
      2 | impl Drop for D { fn drop(&mut self) { println!("  drop {}", self.0); } }
      3 | struct Pair { first: D, second: D }
      4 | fn main() {
      5 |     println!("-- 局部变量");
      6 |     { let _a = D("a"); let _b = D("b"); let _c = D("c"); }
      7 |     println!("-- 结构体字段");
      8 |     { let _p = Pair { first: D("p.first"), second: D("p.second") }; }
      9 |     println!("-- Vec 元素");
     10 |     { let _v = vec![D("v0"), D("v1"), D("v2")]; }
     11 |     println!("-- 临时值在语句结束时 drop");
     12 |     { let _ = D("临时"); println!("  `let _ =` 之后(临时值已经 drop)"); let _keep = D("绑定"); println!("  `let _keep =` 之后(还没 drop)"); }
     13 | }
  编译:成功;警告:
    warning: fields `first` and `second` are never read
     --> <实验目录>/src/u55a.rs:3:15
      |
    3 | struct Pair { first: D, second: D }
      |        ----   ^^^^^     ^^^^^^
      |        |
      |        fields in this struct
      |
      = note: `#[warn(dead_code)]` (part of `#[warn(unused)]`) on by default
    
    warning: 1 warning emitted
  运行(退出码 0):
    -- 局部变量
      drop c
      drop b
      drop a
    -- 结构体字段
      drop p.first
      drop p.second
    -- Vec 元素
      drop v0
      drop v1
      drop v2
    -- 临时值在语句结束时 drop
      drop 临时
      `let _ =` 之后(临时值已经 drop)
      `let _keep =` 之后(还没 drop)
      drop 绑定

所有权与移动见Move 语义真实验证。

提前释放与 forget

实测 · 本机执行输出
  源码:
      1 | struct D(&'static str);
      2 | impl Drop for D { fn drop(&mut self) { println!("  drop {}", self.0); } }
      3 | fn main() {
      4 |     let a = D("early"); println!("drop(a) 之前"); drop(a); println!("drop(a) 之后");
      5 |     let f = D("forgotten"); std::mem::forget(f); println!("forget 之后:不会看到 drop forgotten");
      6 |     let _end = D("end");
      7 | }
  编译:成功
  运行(退出码 0):
    drop(a) 之前
      drop early
    drop(a) 之后
    forget 之后:不会看到 drop forgotten
      drop end
  源码:
      1 | struct D;
      2 | impl Drop for D { fn drop(&mut self) { println!("drop"); } }
      3 | fn main() { let d = D; d.drop(); }
  编译:失败
    error[E0040]: explicit use of destructor method
     --> <实验目录>/src/u55c.rs:3:26
      |
    3 | fn main() { let d = D; d.drop(); }
      |                          ^^^^ explicit destructor calls not allowed
      |
    help: consider using `drop` function
      |
    3 - fn main() { let d = D; d.drop(); }
    3 + fn main() { let d = D; drop(d); }
      |
    
    error: aborting due to 1 previous error
    
    For more information about this error, try `rustc --explain E0040`.

移动之后

实测 · 本机执行输出
  源码:
      1 | struct D(&'static str);
      2 | impl Drop for D { fn drop(&mut self) { println!("  drop {}", self.0); } }
      3 | fn consume(d: D) { println!("  consume 里拿到 {}", d.0); }
      4 | fn main() {
      5 |     let a = D("a"); consume(a);
      6 |     let b = D("b");
      7 |     if std::env::args().count() > 5 { consume(b); } else { println!("  没有移动 b"); }
      8 |     println!("main 结束");
      9 | }
  编译:成功
  运行(退出码 0):
      consume 里拿到 a
      drop a
      没有移动 b
    main 结束
      drop b

panic 与 exit

实测 · 本机执行输出
  编译:成功
  运行(退出码 101):
    即将 panic
      drop guard
    
    thread 'main' (7054972) panicked at <实验目录>/src/u55e.rs:3:58:
    boom
    note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
  源码:持有 D("guard") 后 panic!("boom")
  stdout: 即将 panic |   drop guard
  stderr 第一行: ["thread 'main' (7054972) panicked at <实验目录>/src/u55e.rs:3:58:", 'boom']
  编译:成功
  运行(退出码 0):
    即将 exit
  process::exit 版本的 stdout: 即将 exit (没有 drop guard)

panic 的展开机制不在这篇范围,这里只验证了 drop 是否运行。

分步演示

Drop
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / rustc 1.98.1(brew)/ cargo 1.98.1;单个文件用 rustc 直接编译,需要依赖时用 cargo(经代理从 crates.io 下载);计时用 std::time::Instant,数字每次运行会有小幅抖动。每个实验都是单文件 rustc 编译运行
    • 没有验证:panic 发生在 drop 里的行为(双重 panic 会 abort)、catch_unwind、panic=abort 配置下的 drop、drop 顺序的更多嵌套组合(枚举、闭包捕获)
    京ICP备2026067502号-1京公网安备11010502063487号