mem::ManuallyDrop 类与 mem::forget() 函数

ManuallyDrop 用于抑制编译器自动调用 T 的析构函数 (基于 Drop trait 实现的). 可以用它来调整结构体中成员的 drop 顺序.

看下面一个例子:

use std::mem::ManuallyDrop;

pub struct Sheep {
    name: String,
}

pub struct Cow {
    name: String,
}

pub struct Horse {
    name: String,
}

impl Drop for Sheep {
    fn drop(&mut self) {
        println!("Dropping sheep {}", self.name);
    }
}

impl Drop for Cow {
    fn drop(&mut self) {
        println!("Dropping cow {}", self.name);
    }
}

impl Drop for Horse {
    fn drop(&mut self) {
        println!("Dropping horse {}", self.name);
    }
}

pub struct Animals {
    sheep: ManuallyDrop<Sheep>,
    #[allow(dead_code)]
    horse: Horse,
    cow: ManuallyDrop<Cow>,
}

impl Drop for Animals {
    fn drop(&mut self) {
        println!("Dropping animals");
        unsafe {
            // 手动调用 `drop()` 释放这两个对象.
            ManuallyDrop::drop(&mut self.sheep);
            ManuallyDrop::drop(&mut self.cow);
            // 而 horse 对象会被自动释放.
        }
    }
}

#[allow(unused_variables)]
fn main() {
    let animals = Animals {
        sheep: ManuallyDrop::new(Sheep {
            name: "Doly".to_owned(),
        }),
        horse: Horse {
            name: "Tom".to_owned(),
        },
        cow: ManuallyDrop::new(Cow {
            name: "Jery".to_owned(),
        }),
    };
    // 使用 mem::forget() 会导致内存泄露
    //mem::forget(animals);
}

查看上面例子的打印日志, 可以发现结构体 Animals 的析构顺序是:

  • Animals
    • Sheep
    • Cow
  • Horse

而下面的代码是结构体的一般写法, 其析构顺序是:

  • Animals
  • Sheep
  • Horse
  • Cow
struct Animals {
    sheep: Sheep,
    horse: Horse,
    cow: Cow,
}

mem::forget() 函数

forget() 函数就是利用了 ManuallyDrop 类, 看看该函数的实现:

pub const fn forget<T>(t: T) {
    let _ = ManuallyDrop::new(t);
}

不正确的使用 forget() 函数会产生内存泄露, 看一个例子:

use std::mem;

fn main() {
    let msg = String::from("Hello, Rust");
    mem::forget(msg);
    // msg 的堆内存没有被释放, 产生了内存泄露
}

参考