当前位置: 首页 > news >正文

Rust 登堂 之 Drop 释放资源(十一)

在Rust 中, 我们之所以可以一拳打跑 GC的同时一脚踢翻手动资源回收,主要就归功于 Drop特征,同时它也是只能指针的必备特征之一。

学习目标

如何自动和手动释放资源及执行执行的收尾工作

Rust中的资源回收

在一些无GC 语言中,程序员在一个变量无需再被使用时,需要手动释放它占用的内存资源,如果忘记了,那么就会发生内存泄漏,最终臭名昭著的 OOM 问题可能会发生。

而在Rust 中,你可以指定在一个变量超出作用域时,执行一段特定的代码,最终编译器将帮你自动插入这段收尾代码。这样,就无需在每一个使用该变量的地方,都写一段代码来进行收尾工作和资源释放,不禁让人感叹,Rust 的大腿真粗,

没错,指定这样一段收尾工作靠的就是咱这章的主角- Drop 特征。

一个不那么简单的 Drop 例子

struct  HasDrop1;
struct HasDrop2;impl Drop for HasDrop1 {fn drop(&nut self) {println!("Dropping HasDrop1!");}
}impl Drop fro HasDrop2 {fn drop(&mut self){println!("Dropping HasDrop2!");}
}struct HasTwoDrops {one: HasDrop1,two: HasDrop2,
}impl Drop for HasTwoDrops {fn drop(&mut self) {println!("Dropping HasTwoDrops!");}
}struct Foo;impl Drop for Foo {fn drop(&mut self ){println!("Dropping Foo!")}
}fn main() {let _x = HasTwoDrops {two: HasDrop2,one: HasDrop1,}let _foo = Foo;println!("Running !");
}

上面代码虽然长,但是目的其实很单纯,就是为了观察不同情况下变量级别的,结构体内部字段的 Drop ,有几点值得注意

        Drop 特征中的 drop 方法借用了目标的可变引用,而不是拿走了所有权,这里先设置一个悬念,后边会讲

        结构体中每个字段都有自己的Drop

来看看输出

Running!
Dropping Foo!
Dropping HasTwoDrops!
Dropping HasDrop1!
Dropping HasDrop2!

嗯,结果符合预期,每个资源都成功的执行了收尾工作,虽然 println! 这种收尾工作毫无意义 ==

Drop 的顺序

观察以上输出,我们可以得出以下关于 Drop 顺序的结论

        变量级别,按照逆序的方式,_x 在 _foo 之前的创建,因此在 _x 在 _foo 之后被 drop

        结构体内部,按照顺序的方式,结构体 _x 中的字段按照定义中的顺序依次 drop

没有实现Drop的结构体

实际上,就算你不为 _x 结构体实现 Drop 特征,它内部的两个字段依然会调用 drop,移除以下代码,并观察输出

impl Drop for HasTwoDrops {fn drop(&mut self) {println!("Dropping HasTwoDrops!");}
}

原因在于,Rust 自动为几乎所有类型都实现了 Drop 特征,因此就算你不手动为结构体实现 Drop 它依然会调用默认实现的 drop 函数,同时再调用每个字段的 drop方法, 最终打印出:

Dropping HasDrop1!
Dropping HasDrop2!

手动回收

当使用智能指针来管理锁的时候,你可能希望提前释放这个锁,然后让其它代码能及时获得锁,此时就需要提前去手动 drop ,但是在之前我们提到一个悬念, Drop::drop  只是借用了目标值的可变引用,所以,就算你提前调用了 drop ,后面的代码依然可以使用目标值,但是这就会访问一个并不存在的值,非常不安全,好在Rust 会阻止你。

#[derive(Debug)]
struct Foo;impl Drop for Foo {fn drop(&mut self){println!("Dropping Foo!")}
}fn main() {let foo = Foo;foo.drop();println!("Running!:{:?}",foo);
}

报错如下

error[E0040]: explicit use of destructor method
  --> src/main.rs:37:9
   |
37 |     foo.drop();
   |     ----^^^^--
   |     |   |
   |     |   explicit destructor calls not allowed
   |     help: consider using `drop` function: `drop(foo)`

如上所示,编译器直接阻止了我们调用 Drop 特征的 drop 方法,原因是对于 Rust 而言,不允许显式地调用析构函数(这是一个用来清理实例的通用编程概念), 好在在报酬的时候,编译器还给出了一个提示:使用 drop 函数

针对编译器提示的 drop 函数 ,我们可以大胆推测下: 它能狗拿走目标值的所有权,现在来看看这个猜测正确与否,以下是 std::mem::drop 函数的签名

pub fn drop<T>(_x: T)

如上所示,drop 函数确实拿走了 目标值的所有权,来验证下:

fn main() {let foo = Foo;drop(foo);// 以下代码会报错: 借用了所有权被转移的值// println!("Running!:{:?}",foo);
}

Bingo ,完美拿走了所有权,而且这种实现保证了后续的使用必定会导致编译错误,因此非常安全!

细心的同学可能已经注意到,这里直接调用了drop 函数,并没有引入任何模块信息,原因是该函数在 std::prelude 里

事实上,能被显式调用的 drop(_x) 函数知识个空函数,在拿走目标值的所有权后没有任何操作,而由于其持有目标值的所有权,在 drop(_x) 函数结束之际,编译器会执行_x 真正的析构函数,从而完成释放资源的操作,换句话说,drop(_x)函数只是帮助目标值的所有者提前离开了作用域 https://doc.rust-lang.org/std/mem/fn.drop.html

Drop使用场景

对于Drop 而言,主要有两个功能

        回收内存资源

        执行一些收尾工作

对于第二点,在之前我们已经详细介绍过,因此这里主要对第一点进行下简单说明

在绝大多数情况下,我们都无需手动去drop 以回收内存宅院,因为 Rust 会自动帮我们完成这些工作,它甚至会对复杂类型的每个字段都单独的调用 drop 进行回收!但是确实有极少数情况,需要你来回收资源的,例如文件描述符,网络socket 等,当这些值超出作用域不再使用时,就需要进行关闭以释放相关的资源,在这些情况下,就需要使用者自己来解决Drop 的问题。

互斥的Copy 和 Drop

我们无法为一个类型同时实现Copy 和 Drop 特征,因为实现了Copy 特征的类型会被编译器隐式的复制,因此非常难以预测析构函数执行的时间和频率。因此这些实现了 Copy的类型无法拥有析构函数

#[derive(Copy)]
struct Foo;impl Drop for Foo {fn drop(&mut self) {println!("Dropping Foo!")}   
}

以上代码报错如下

error[E0184]: the trait `Copy` may not be implemented for this type; the type has a destructor
  --> src/main.rs:24:10
   |
24 | #[derive(Copy)]
   |          ^^^^ Copy not allowed on types with destructors

总结

Drop 可以用于许多方面,来使得资源清理及收尾工作变得方便和安全,甚至可以用其创建我们自己的内存分配器! 通过 Drop 特征和 Rust 所有权系统,你无需担心之后的代码清理,Rust 会自动考虑这些问题。

我们也无需担心以外的清理掉仍在使用的值,这会造成编译器错误:所有权系统确保引用总是有效的,也会确保 drop 只会在值不再被使用时被调用一次


文章转载自:

http://7hXhbsQW.tzjqm.cn
http://Ks0Lamqn.tzjqm.cn
http://UbQyeLDc.tzjqm.cn
http://zFqzxYEV.tzjqm.cn
http://TmOLwKB8.tzjqm.cn
http://RSeGcRJ5.tzjqm.cn
http://bn7ra3Fp.tzjqm.cn
http://ZAwPaDOB.tzjqm.cn
http://dZXocnAY.tzjqm.cn
http://2hVtMoeX.tzjqm.cn
http://de5p2Y7v.tzjqm.cn
http://cr8p5fdy.tzjqm.cn
http://XQQmyLOd.tzjqm.cn
http://AgoYQw08.tzjqm.cn
http://GENuNS2Q.tzjqm.cn
http://mWg1P3rk.tzjqm.cn
http://Mmrhpmqs.tzjqm.cn
http://4sQw4f1M.tzjqm.cn
http://I7JK8pWA.tzjqm.cn
http://GQysFiNr.tzjqm.cn
http://hynsUBWO.tzjqm.cn
http://sayZWHCj.tzjqm.cn
http://vuUKSMkF.tzjqm.cn
http://SuT06mGZ.tzjqm.cn
http://fLVhoDIg.tzjqm.cn
http://CnKtA38I.tzjqm.cn
http://yUURu2HO.tzjqm.cn
http://6Uy8uBBl.tzjqm.cn
http://Qf9a1z2K.tzjqm.cn
http://QzS33ePw.tzjqm.cn
http://www.dtcms.com/a/371832.html

相关文章:

  • 开关电源的原理、结构和实物入门篇-超简单解读
  • Environments
  • 上架商品合规流程有多条,有的长,有的短,有的需要审核,校验商品的合规性
  • 简单聊一聊js
  • 合格齿轴工艺工程师要修炼哪些功法?
  • LwIP入门实战 — 5 LwIP 的内存管理
  • 【三维生成】Matrix-3D:全向可探索的三维世界生成
  • DispatcherServlet 初始化过程:SpringMVC 的 “启动引擎” 详解
  • Simulink中使用Test sequence单元测试
  • 20250907-02:LangChain 架构和LangChain 生态系统包是什么
  • 大数据(非结构化数据,Spark,MongoDB)
  • FastAPI + LangChain 和 Spring AI + LangChain4j
  • Python基础语法篇:整数和浮点数,加减乘除怎么算?
  • 现成的AI模型:训练+评估框架汇总
  • 服务器断电引起的一例ORA-01207故障处理----惜分飞
  • 《MySQL基础——用户管理》
  • 【Linux】系统部分——进程间通信2(共享内存)
  • 【温室气体数据集】全球协作碳柱观测网络 COCCON
  • STM32 JLINK下载失败解决方案
  • JavaScript 中十种数组拷贝方法(从浅拷贝到深拷贝)
  • 04.事件中心模块
  • 【直接套模板】如何用 Web of Science 精准检索文献?
  • MCP与A2A
  • 数据库索引设计:在 MongoDB 中创建高效索引的策略
  • Shell 秘典(卷十)—— 服务器资源自动化监控脚本的设计与实现
  • 能源电力方向 的创业idea1
  • tf_keras包
  • PyTorch Lightning(训练评估框架)
  • Python进程,线程
  • java设计模式二、工厂