48_Drop Trait

626 阅读3分钟

1. 概述

当一个数据类型实现Drop Trait之后,可以让我们自定义当值离开作用域时发生的动作。

通常发生的动作包括:文件、网络资源的释放等。任何类型都可以实现Drop trait,Drop Trait只要求实现drop方法,drop的参数是对self的可变引用。

Drop trait在预导入模块(prelude)里。

我们先看一个示例代码:

struct CustomartPointer {
    data: String,
}

impl Drop for CustomartPointer {
    fn drop(&mut self) {
        println!("Dropping CustomSmartPointer with data `{}` !", self.data)
    }
}

fn main() {
    let c = CustomartPointer { data: String::from("my  stuff") };
    let d = CustomartPointer { data: String::from("other  stuff") };

    println!("CusomSmartPointers created")
}

执行程序之后,输出以下内容

CusomSmartPointers created
Dropping CustomSmartPointer with data `other  stuff` !
Dropping CustomSmartPointer with data `my  stuff` !

这表明,两个变量离开作用域的时候调用了drop方法。

2. 使用std::mem::drop来提前drop值

我们很难直接自动调用drop功能,其实也没有必要,因为Drop trait的目的就是进行自动的释放逻辑处理。

此外,rust也不允许手动调用Drop trait的drop方法,但可以调用标准库的std::mem::drop函数,该函数也在预导入模块中,来提前drop值,相当于提前调用了Drop trait的drop方法。

如下示例代码:

let c = CustomartPointer { data: String::from("my  stuff") };
drop(c);
let d = CustomartPointer { data: String::from("other  stuff") };

println!("CusomSmartPointers created")

这时候,输入内容如下

Dropping CustomSmartPointer with data `my  stuff` !
CusomSmartPointers created
Dropping CustomSmartPointer with data `other  stuff` !

这时候,你可能会有疑问:提前调用drop函数,那么会不会出现重复释放(double free)的错误呢?答案是不会的,rust的设计很安全,它的所有权系统会保证引用的有效,而drop方法也只会在不再使用该值的时候只调用一次。

注意点

当实现Drop trait时,需要注意以下几点:

  1. Drop trait对于类型的所有字段都会自动调用drop方法,无需手动处理。如果某个字段实现了自己的Drop trait,则在类型的drop方法中不需要再手动调用该字段的drop方法。
  2. Drop trait不能显式调用,不能在代码中直接调用类型的drop方法。只能依靠Rust的所有权系统自动调用。
  3. Drop trait的drop方法不能抛出异常,即不能在drop方法中使用panic!宏或调用可能会panic的函数。如果需要处理可能出现的错误,可以使用Result类型。
  4. Drop trait只能用于实现清理资源的操作,不应该用于修改值或执行其他逻辑。在drop方法中应该专注于资源释放,避免执行可能导致错误的操作。
  5. 如果在类型中使用了Rc或Arc来共享所有权,则不会调用drop方法,因为资源的释放由引用计数器控制。如果需要在最后一个所有权者离开作用域时执行特定的清理操作,可以使用Rc::downgrade和Weak类型来实现。
  6. Drop trait的drop方法会按照值的创建顺序的逆序执行,即后创建的值会先执行drop方法,先创建的值会后执行drop方法。

3. 总结

  • Rust的Drop trait用于自定义类型在离开作用域时的清理行为。
  • 通过实现Drop trait并定义drop方法,可以在值被销毁时执行自定义的清理代码。
  • drop方法的参数是对self的可变引用。
  • Rust会自动调用类型的drop方法,无需手动调用。
  • 使用std::mem::drop函数可以提前释放值,相当于手动调用drop方法。
  • Rust的所有权系统保证了drop方法只会被调用一次,避免了重复释放的错误。