喜好的话别忘了点赞、收藏加关注哦(加关注即可阅读全文),对接下来的教程有兴趣的可以关注专栏。谢谢喵!(=・ω・=)
说句题外话,这篇文章一共5721个字,是我截至如今写的最长的一篇文章,看我这么努力,还不点赞、收藏加关注?
2.8.1. 显式析构函数的问题
添加显式析构函数时会遇到问题:
- 当某一范例实现了Drop,在析构函数中无法将该范例的任何字段移出。因为在显式析构函数运行后,drop()仍会被调用,它担当&mut self,要求self的全部部门都没被移动。
- Drop接收的是&mut self而不是self,因此Drop无法实现简单地调用显式析构函数并忽略其结果(因为Drop不拥有self)
以上一篇文章的例子为基础,如果我们加上既实现了Drop trait,又写了close方法:
- use std::os::fd::AsRawFd;
- use std::fs::{File as StdFile, OpenOptions, metadata};
- use std::io::Error;
-
- /// 一个表示文件句柄的类型
- struct File {
- /// 文件名
- name: String,
- /// 文件描述符
- fd: i32,
- }
-
- impl File {
- /// 一个构造函数,打开一个文件并返回一个 File 实例
- fn open(name: &str) -> Result<File, Error> {
- // 使用 OpenOptions 打开文件,具备读写权限
- let file: StdFile = OpenOptions::new()
- .read(true)
- .write(true)
- .open(name)?;
-
- // 获取文件描述符
- let fd: i32 = file.as_raw_fd();
-
- // 返回一个 File 实例
- Ok(File {
- name: name.to_string(),
- fd,
- })
- }
-
- /// 一个显式的析构函数,关闭文件并返回任何错误
- fn close(self) -> Result<(), Error> {
- // 使用 FromRawFd 将 fd 转换回 File
- let file: std::fs::File = unsafe {
- std::os::unix::io::FromRawFd::from_raw_fd(self.fd)
- };
-
- // 刷新文件数据到磁盘
- file.sync_all()?;
-
- // 将文件截断为 0 字节
- file.set_len(0)?;
-
- // 再次刷新文件
- file.sync_all()?;
-
- // 丢弃文件实例,它会自动关闭文件
- drop(file);
-
- // 返回成功
- Ok(())
- }
- }
-
- //实现drop trait
- impl Drop for File {
- fn drop(&mut self) {
- let _ = self.close(); //调用close方法来丢弃
- println!("File dropped");
- }
- }
-
- fn main() {
- // 创建一个名为 "test.txt" 的文件,并写入一些内容
- std::fs::write("test.txt", "Hello, world!").unwrap();
-
- // 打开文件并获取 File 实例
- let file: File = File::open("test.txt").unwrap();
-
- // 打印文件名和 fd
- println!("File name: {}, fd: {}", file.name, file.fd);
-
- // 关闭文件并处理任何错误
- match file.close() {
- Ok(()) => println!("File closed successfully"),
- Err(e) => println!("Error closing file: {}", e),
- }
-
- // 检查关闭后的文件大小
- let metadata = metadata("test.txt").unwrap();
- println!("File size: {} bytes", metadata.len());
- }
复制代码 输出:
- error[E0507]: cannot move out of `*self` which is behind a mutable reference
- --> src/main.rs:59:17
- |
- 59 | let _ = self.close(); //调用close方法来丢弃
- | ^^^^ ------- `*self` moved due to this method call
- | |
- | move occurs because `*self` has type `File`, which does not implement the `Copy` trait
- |
- note: `File::close` takes ownership of the receiver `self`, which moves `*self`
- --> src/main.rs:33:14
- |
- 33 | fn close(self) -> Result<(), Error> {
- | ^^^^
- note: if `File` implemented `Clone`, you could clone the value
- --> src/main.rs:6:1
- |
- 6 | struct File {
- | ^^^^^^^^^^^ consider implementing `Clone` for this type
- ...
- 59 | let _ = self.close(); //调用close方法来丢弃
- | ---- you could clone this value
复制代码 报错信息显示无法从*self中移出值,因为它位于&mut self后面。
2.8.2. 解决方案
首先需要说明的是没有完美的解决方案,我们只能努力补充。
解决方案1:把结构体包装Option<T>里然后再套一层结构体
我们可以将顶层方案作为包装了Option<T>的新范例,如许Option<T>内部持有一个范例,这个范例包含全部的字段。
这个时候我们就需要两个析构函数,外边一个里面一个。在这两个析构函数中使用Option::take函数来获取数据全部权从而移除值。
由于内部范例没有实现Drop,以是你可以获取全部字段的全部权。
缺点:想在顶层范例上提供的全部方法,如今都必须添加通过Option<T>这层壳来获取内部范例上字段的代码。
我们在上文的代码例基础上进行修改:
步骤1:改掉File的定义,套一层壳
首先我们得把两个字段移到另一个结构体里,套在Option<T>中作为File结构体的字段
- /// 一个表示文件句柄的类型
- struct InnerFile {
- /// 文件名
- name: String,
- /// 文件描述符
- fd: i32,
- }
-
- /// 给InnerFile套了一个壳
- struct File {
- /// 把InnerFile包装在Option<T>中
- inner: Option<InnerFile>,
- }
复制代码 步骤2:修改File上的方法
File上有两个方法,我们都需要添加通过Option<T>这层壳来获取内部范例上字段的代码。
首先是open方法:
- /// 一个构造函数,打开一个文件并返回一个 File 实例
- fn open(name: &str) -> Result<File, Error> {
- // 使用 OpenOptions 打开文件,具备读写权限
- let file: StdFile = OpenOptions::new()
- .read(true)
- .write(true)
- .open(name)?;
-
- // 获取文件描述符
- let fd: i32 = file.as_raw_fd();
-
- // 返回一个 File 实例
- Ok(File {
- inner: Some( InnerFile {
- name: name.to_string(),
- fd,
- })
- })
- }
复制代码
- 由于这个代码只有返回值计划了File结构体,以是也只有返回值需要改
接下来是close方法:
- /// 一个显式的析构函数,关闭文件并返回任何错误
- fn close(mut self) -> Result<(), Error> { // 记得self要变为mut self,否则take不了
- // 使用模式匹配提取出字段的值
- if let Some(inner) = self.inner.take() {
- let name = inner.name;
- let fd = inner.fd;
- println!("Closing file: {} with fd: {}", name, fd);
-
- // 使用 FromRawFd 将 fd 转换回 File
- let file: std::fs::File = unsafe {
- std::os::unix::io::FromRawFd::from_raw_fd(fd)
- };
-
- // 刷新文件数据到磁盘
- file.sync_all()?;
-
- // 将文件截断为 0 字节
- file.set_len(0)?;
-
- // 再次刷新文件
- file.sync_all()?;
-
- // 丢弃文件实例,它会自动关闭文件
- drop(file);
-
- // 返回成功
- Ok(())
- } else {
- // 如果inner字段是None,说明文件已经被关闭或丢弃,返回错误
- Err(Error::new(
- std::io::ErrorKind::Other,
- "File is already closed",
- ))
- }
- }
复制代码
- 参数传进去之后先得通过模式匹配来获取字段的值
- 如果如果inner字段是None,也就是模式匹配不乐成的环境下,我们需要自己写一个错误返回
步骤3:修改Drop trait的实现
Drop::drop方法需要修改:
- fn drop(&mut self) {
- // 使用模式匹配获取字段值
- if let Some(inner) = self.inner.take() {
- let name = inner.name;
- let fd = inner.fd;
- println!("Dropping file: {} (fd: {})", name, fd);
-
- // 使用 FromRawFd 将 fd 转换回 File
- let file: std::fs::File = unsafe {
- std::os::unix::io::FromRawFd::from_raw_fd(fd)
- };
-
- // 丢弃file实例
- drop(file);
- } else {
- // 如果inner字段是None的话,说明文件已经被丢弃或关闭,不做任何操作
- }
- }
复制代码
- 参数传进去之后先得通过模式匹配来获取字段的值
- 如果inner字段是None的话,说明文件已经被丢弃或关闭,不做任何操作
步骤4:微修主函数
主函数中需要提取字段值的地方需要修改:
- fn main() {
- // ...前文无修改,已省略
- // 打印文件名和 fd (这里需要修改)
- println!("File name: {}, fd: {}",
- file.inner.as_ref().unwrap().name,
- file.inner.as_ref().unwrap().fd
- );
- // ...后文无修改,已省略
- }
复制代码
- 原始范例是 Option<InnerFile>,调用.as_ref()后变成Option<&InnerFile>
- 变成了Option<&InnerFile>再使用unwrap提取出来的值就是引用而不是全部的值
- file.inner是一个Option<InnerFile>范例。而直接访问Option的值需要转移全部权或匹配处理(例如通过 take() 或 unwrap()),这会销毁Option的内部值,以是得要as_ref
团体代码
- use std::os::fd::AsRawFd; use std::fs::{File as StdFile, OpenOptions, metadata}; use std::io::Error; /// 一个表示文件句柄的类型
- struct InnerFile {
- /// 文件名
- name: String,
- /// 文件描述符
- fd: i32,
- }
-
- /// 给InnerFile套了一个壳
- struct File {
- /// 把InnerFile包装在Option<T>中
- inner: Option<InnerFile>,
- }
- impl File { /// 一个构造函数,打开一个文件并返回一个 File 实例 fn open(name: &str) -> Result<File, Error> { // 使用 OpenOptions 打开文件,具备读写权限 let file: StdFile = OpenOptions::new() .read(true) .write(true) .open(name)?; // 获取文件形貌符 let fd: i32 = file.as_raw_fd(); // 返回一个 File 实例 Ok(File { inner: Some( InnerFile { name: name.to_string(), fd, }) }) } /// 一个显式的析构函数,关闭文件并返回任何错误 fn close(mut self) -> Result<(), Error> { // 记得self要变为mut self,否则take不了 // 使用模式匹配提取出字段的值 if let Some(inner) = self.inner.take() { let name = inner.name; let fd = inner.fd; println!("Closing file: {} with fd: {}", name, fd); // 使用 FromRawFd 将 fd 转换回 File let file: std::fs::File = unsafe { std::os::unix::io::FromRawFd::from_raw_fd(fd) }; // 革新文件数据到磁盘 file.sync_all()?; // 将文件截断为 0 字节 file.set_len(0)?; // 再次革新文件 file.sync_all()?; // 丢弃文件实例,它会主动关闭文件 drop(file); // 返回乐成 Ok(()) } else { // 如果inner字段是None,说明文件已经被关闭或丢弃,返回错误 Err(Error::new( std::io::ErrorKind::Other, "File is already closed", )) } } } // 实现drop trait,用于在值脱离作用域时运行的一些代码 impl Drop for File { fn drop(&mut self) { // 使用模式匹配获取字段值 if let Some(inner) = self.inner.take() { let name = inner.name; let fd = inner.fd; println!("Dropping file: {} (fd: {})", name, fd); // 使用 FromRawFd 将 fd 转换回 File let file: std::fs::File = unsafe { std::os::unix::io::FromRawFd::from_raw_fd(fd) }; // 丢弃file实例 drop(file); } else { // 如果inner字段是None的话,说明文件已经被丢弃或关闭,不做任何操作 } } } fn main() { // 创建一个名为 "test.txt" 的文件,并写入一些内容 std::fs::write("test.txt", "Hello, world!").unwrap(); // 打开文件并获取 File 实例 let file: File = File::open("test.txt").unwrap(); // 打印文件名和 fd (这里需要修改) println!("File name: {}, fd: {}", file.inner.as_ref().unwrap().name, file.inner.as_ref().unwrap().fd ); // 关闭文件并处理任何错误 match file.close() { Ok(()) => println!("File closed successfully"), Err(e) => println!("Error closing file: {}", e), } // 查抄关闭后的文件巨细 let metadata = metadata("test.txt").unwrap(); println!("File size: {} bytes", metadata.len()); }
复制代码 解决方案2:把字段包装在Option<T>里
我们也可以保持结构体不变,把每个字段的值都套在Option<T>里,需要获取全部权使用时用Option::take就可以,需要引用时用.as_ref()加.unwrap()就可以。
如果范例具有合理的空值,那么效果很好。
缺点:如果你必须将险些每个字段都包装在Option中,然后对这些字段的每次访问都进行匹配的unwrap就会使代码变得很繁琐。
我们在上文的代码例基础上进行修改:
步骤1:改掉File的定义
为每一个字段添加一层Option<T>:
- /// 一个表示文件句柄的类型
- struct File {
- /// 文件名
- name: Option<String>,
- /// 文件描述符
- fd: Option<i32>,
- }
复制代码 步骤2:修改File上的方法
File上有两个方法,我们都需要添加通过Option<T>这层壳来获取内部范例上字段的代码。
首先是open方法:
- /// 一个构造函数,打开一个文件并返回一个 File 实例
- fn open(name: &str) -> Result<File, Error> {
- // 使用 OpenOptions 打开文件,具备读写权限
- let file: StdFile = OpenOptions::new()
- .read(true)
- .write(true)
- .open(name)?;
-
- // 获取文件描述符
- let fd: i32 = file.as_raw_fd();
-
- // 返回一个 File 实例
- Ok(File {
- name: Some(name.to_string()),
- fd: Some(fd),
- })
- }
复制代码
- open方法的参数没有涉及到File结构体,以是接收参数部门不用修改
- open方法的返回值涉及到了File,得为每个字段添上Some变体
其次是close方法:
- /// 一个显式的析构函数,关闭文件并返回任何错误
- fn close(mut self) -> Result<(), Error> {
- // 模式匹配,并使用std::mem::take取出name字段的值
- if let Some(name) = std::mem::take(&mut self.name) {
- //模式匹配,并使用std::mem::take取出fd字段的值
- if let Some(fd) = std::mem::take(&mut self.fd) {
- // 打印
- println!("Closing file: {} with fd: {}", name, fd);
-
- // 使用 FromRawFd 将 fd 转换回 File
- let file: std::fs::File = unsafe {
- std::os::unix::io::FromRawFd::from_raw_fd(fd)
- };
-
- // 刷新文件数据到磁盘
- file.sync_all()?;
-
- // 将文件截断为 0 字节
- file.set_len(0)?;
-
- // 再次刷新文件
- file.sync_all()?;
-
- // 丢弃文件实例,它会自动关闭文件
- drop(file);
-
- // 返回成功
- Ok(())
- } else {
- // 如果fd字段是None,说明文件已经被关闭或丢弃,返回一个错误
- Err(Error::new(
- std::io::ErrorKind::Other,
- "File descriptor already dropped or taken",
- ))
- }
- } else {
- // 如果name字段是None,说明文件已经被关闭或丢弃,返回一个错误
- Err(Error::new(
- std::io::ErrorKind::Other,
- "File name already dropped or taken",
- ))
- }
- }
复制代码
- 参数要先经过模式匹配,并使用std::mem::take取出里面的值
- 如果任意字段是None,说明文件已经被关闭或丢弃,返回一个错误
步骤3:修改Drop trait的实现
- fn drop(&mut self) {
- // 使用模式匹配获取字段值
- if let Some(name) = self.name.take() {
- if let Some(fd) = self.fd.take() {
- println!("Dropping file: {} (fd: {})", name, fd);
-
- // 使用 FromRawFd 将 fd 转换回
- Filelet file: std::fs::File = unsafe {
- std::os::unix::io::FromRawFd::from_raw_fd(fd)
- };
-
- // 丢弃file实例
- drop(file);
- } else {
- // 如果fd字段是None,说明文件已经被关闭或丢弃,不做任何操作
- }
- } else {
- // 如果name字段是None,说明文件已经被关闭或丢弃,不做任何操作
- }
- }
复制代码
- 参数要先经过模式匹配,并使用std::mem::take取出里面的值
- 如果任意字段是None,说明文件已经被关闭或丢弃,不做任何操作
步骤4:微修主函数
- fn main() {
- // ...前文无修改,已省略
- // 打印文件名和 fd (这里需要修改)
- println!("File name: {}, fd: {}",
- file.name.as_ref().unwrap(),
- file.fd.as_ref().unwrap()
- );
- // ...后文无修改,已省略
- }
复制代码
- 原始范例被Option<T>包裹,调用.as_ref()后获得里面值的引用
- 变成了引用之后再使用unwrap提取出来的值就是引用而不是全部的值
- 而直接访问Option的值需要转移全部权或匹配处理(例如通过 take() 或 unwrap()),这会销毁Option的内部值,以是得要as_ref
团体代码
- use std::os::fd::AsRawFd; use std::fs::{File as StdFile, OpenOptions, metadata}; use std::io::Error; /// 一个表示文件句柄的类型
- struct File {
- /// 文件名
- name: Option<String>,
- /// 文件描述符
- fd: Option<i32>,
- }
- impl File { /// 一个构造函数,打开一个文件并返回一个 File 实例 fn open(name: &str) -> Result<File, Error> { // 使用 OpenOptions 打开文件,具备读写权限 let file: StdFile = OpenOptions::new() .read(true) .write(true) .open(name)?; // 获取文件形貌符 let fd: i32 = file.as_raw_fd(); // 返回一个 File 实例 Ok(File { name: Some(name.to_string()), fd: Some(fd), }) } /// 一个显式的析构函数,关闭文件并返回任何错误 fn close(mut self) -> Result<(), Error> { // 模式匹配,并使用使用std::mem::take取出name字段的值 if let Some(name) = std::mem::take(&mut self.name) { //模式匹配,并使用使用std::mem::take取出fd字段的值 if let Some(fd) = std::mem::take(&mut self.fd) { // 打印 println!("Closing file: {} with fd: {}", name, fd); // 使用 FromRawFd 将 fd 转换回 File let file: std::fs::File = unsafe { std::os::unix::io::FromRawFd::from_raw_fd(fd) }; // 革新文件数据到磁盘 file.sync_all()?; // 将文件截断为 0 字节 file.set_len(0)?; // 再次革新文件 file.sync_all()?; // 丢弃文件实例,它会主动关闭文件 drop(file); // 返回乐成 Ok(()) } else { // 如果fd字段是None,说明文件已经被关闭或丢弃,返回一个错误 Err(Error::new( std::io::ErrorKind::Other, "File descriptor already dropped or taken", )) } } else { // 如果name字段是None,说明文件已经被关闭或丢弃,返回一个错误 Err(Error::new( std::io::ErrorKind::Other, "File name already dropped or taken", )) } } } // 实现drop trait,用于在值脱离作用域时运行的一些代码 impl Drop for File { fn drop(&mut self) { // 使用模式匹配获取字段值 if let Some(name) = self.name.take() { if let Some(fd) = self.fd.take() { println!("Dropping file: {} (fd: {})", name, fd); // 使用 FromRawFd 将 fd 转换回 Filelet file: std::fs::File = unsafe { std::os::unix::io::FromRawFd::from_raw_fd(fd) }; // 丢弃file实例 drop(file); } else { // 如果fd字段是None,说明文件已经被关闭或丢弃,不做任何操作 } } else { // 如果name字段是None,说明文件已经被关闭或丢弃,不做任何操作 } } } fn main() { // 创建一个名为 "test.txt" 的文件,并写入一些内容 std::fs::write("test.txt", "Hello, world!").unwrap(); // 打开文件并获取 File 实例 let file: File = File::open("test.txt").unwrap(); // 打印文件名和 fd (这里需要修改) println!("File name: {}, fd: {}", file.name.as_ref().unwrap(), file.fd.as_ref().unwrap() ); // 关闭文件并处理任何错误 match file.close() { Ok(()) => println!("File closed successfully"), Err(e) => println!("Error closing file: {}", e), } // 查抄关闭后的文件巨细 let metadata = metadata("test.txt").unwrap(); println!("File size: {} bytes", metadata.len()); }
复制代码 方法3:将数据持有在ManuallyDrop范例内
将数据持有在ManuallyDrop范例内,它会解引用内部范例,不必再使用unwrap。
在drop中进行销毁时,可使用ManuallyDrop::take来获取全部权。
缺点:ManuallyDrop::take是不安全的,需要放在unsafe块中。
我们在上文的代码例基础上进行修改:
步骤1:改掉File的定义
为每一个字段添加一层Option<T>:
- /// 一个表示文件句柄的类型
- struct File {
- /// 文件名
- name: ManuallyDrop<String>,
- /// 文件描述符
- fd: ManuallyDrop<i32>,
- }
复制代码 步骤2:修改File上的方法
File上有两个方法,我们都需要添加通过Option<T>这层壳来获取内部范例上字段的代码。
首先是open方法:
- /// 一个构造函数,打开一个文件并返回一个 File 实例
- fn open(name: &str) -> Result<File, Error> {
- // 使用 OpenOptions 打开文件,具备读写权限
- let file: StdFile = OpenOptions::new()
- .read(true)
- .write(true)
- .open(name)?;
-
- // 获取文件描述符
- let fd: i32 = file.as_raw_fd();
-
- // 返回一个 File 实例
- Ok(File {
- name: ManuallyDrop::new(name.to_string()),
- fd: ManuallyDrop::new(fd),
- })
- }
复制代码
- open方法的参数没有涉及到File结构体,以是接收参数部门不用修改
- open方法的返回值涉及到了File,每个字段都得用ManuallyDrop::new来传值
其次是close方法:
- /// 一个显式的析构函数,关闭文件并返回任何错误
- fn close(mut self) -> Result<(), Error> {
- // 使用std::mem::replace将name字段替换为一个空字符串,把原来的值给name
- let name =
- std::mem::replace(&mut self.name, ManuallyDrop::new("".to_string()));
-
- // 使用std::mem::replace将fd字段替换为一个无效的值(-1),把原来的值给fd
- let fd =
- std::mem::replace(&mut self.fd, ManuallyDrop::new(0));
-
- // 打印
- println!("Closing file: {:?} with fd: {:?}", name, fd);
-
- // 使用 FromRawFd 将 fd 转换回 File
- let file: std::fs::File = unsafe {
- std::os::unix::io::FromRawFd::from_raw_fd(*fd) //这里fd要先解引用
- };
-
- // 刷新文件数据到磁盘
- file.sync_all()?;
-
- // 将文件截断为 0 字节
- file.set_len(0)?;
-
- // 再次刷新文件
- file.sync_all()?;
-
- // 丢弃文件实例,它会自动关闭文件
- drop(file);
-
- // 返回成功
- Ok(())
- }
复制代码
- 使用std::mem::replace将name字段替换为一个空字符串,把原来的值给name
- 使用std::mem::replace将fd字段替换为一个无效的值(-1),把原来的值给fd
- std:
s::unix::io::FromRawFd::from_raw_fd(*fd)中参数要先解引用,也就是写*fd
步骤3:修改Drop trait的实现
- fn drop(&mut self) {
- // 使用ManuallyDrop::take取出name字段的值,并检查是否是空字符串
- let name = unsafe { ManuallyDrop::take(&mut self.name) };
-
- // 使用ManuallyDrop::take取出fd字段的值,并检查是否是无效的值
- let fd = unsafe { ManuallyDrop::take(&mut self.fd) };
-
- //打印
- println!("Dropping file: {:?} (fd: {:?})", name, fd);
-
- // 如果fd字段不是无效的值,说明文件还没有被关闭或丢弃,需要执行丢弃操作
- if fd != -1 || !name.is_empty() {
- let file = unsafe { std::fs::File::from_raw_fd(fd) };
- // 丢弃
- drop(file);
- }
- }
复制代码
- 使用ManuallyDrop::take取出name和fd字段的值,并查抄是否是空字符串或无效的值
- 如果fd字段不是无效的值(不是-1),或是name字段不是空字符串,就说明文件还没有被关闭或丢弃,需要实行丢弃操作
- 其实这里不用两个判断条件(fd != -1 || !name.is_empty()),一个就够了,因为name和fd字段的值的变化是一起的,一个无效就代表着整个结构体都还未被清理。
步骤4:微修主函数
- fn main() {
- // ...前文无修改,已省略
- // 打印文件名和 fd (这里需要修改)
- println!("File name: {}, fd: {}", *file.name, *file.fd);
- // ...后文无修改,已省略
- }
复制代码
团体代码
- use std::os::fd::{AsRawFd, FromRawFd}; use std::fs::{File as StdFile, OpenOptions, metadata}; use std::io::Error; use std::mem::ManuallyDrop; /// 一个表示文件句柄的类型
- struct File {
- /// 文件名
- name: ManuallyDrop<String>,
- /// 文件描述符
- fd: ManuallyDrop<i32>,
- }
- impl File { /// 一个构造函数,打开一个文件并返回一个 File 实例 fn open(name: &str) -> Result<File, Error> { // 使用 OpenOptions 打开文件,具备读写权限 let file: StdFile = OpenOptions::new() .read(true) .write(true) .open(name)?; // 获取文件形貌符 let fd: i32 = file.as_raw_fd(); // 返回一个 File 实例 Ok(File { name: ManuallyDrop::new(name.to_string()), fd: ManuallyDrop::new(fd), }) } /// 一个显式的析构函数,关闭文件并返回任何错误 fn close(mut self) -> Result<(), Error> { // 使用std::mem::replace将name字段替换为一个空字符串,把原来的值给name let name = std::mem::replace(&mut self.name, ManuallyDrop::new("".to_string())); // 使用std::mem::replace将fd字段替换为一个无效的值(-1),把原来的值给fd let fd = std::mem::replace(&mut self.fd, ManuallyDrop::new(-1)); // 打印 println!("Closing file: {:?} with fd: {:?}", name, fd); // 使用 FromRawFd 将 fd 转换回 File let file: std::fs::File = unsafe { std::os::unix::io::FromRawFd::from_raw_fd(*fd) //这里fd要先解引用 }; // 革新文件数据到磁盘 file.sync_all()?; // 将文件截断为 0 字节 file.set_len(0)?; // 再次革新文件 file.sync_all()?; // 丢弃文件实例,它会主动关闭文件 drop(file); // 返回乐成 Ok(()) } } // 实现drop trait,用于在值脱离作用域时运行的一些代码 impl Drop for File { fn drop(&mut self) { // 使用ManuallyDrop::take取出name字段的值,并查抄是否是空字符串 let name = unsafe { ManuallyDrop::take(&mut self.name) }; // 使用ManuallyDrop::take取出fd字段的值,并查抄是否是无效的值 let fd = unsafe { ManuallyDrop::take(&mut self.fd) }; //打印 println!("Dropping file: {:?} (fd: {:?})", name, fd); // 如果fd字段不是无效的值,说明文件还没有被关闭或丢弃,需要实行丢弃操作 if fd != -1 || !name.is_empty() { let file = unsafe { std::fs::File::from_raw_fd(fd) }; // 丢弃 drop(file); } } } fn main() { // 创建一个名为 "test.txt" 的文件,并写入一些内容 std::fs::write("test.txt", "Hello, world!").unwrap(); // 打开文件并获取 File 实例 let file: File = File::open("test.txt").unwrap(); // 打印文件名和 fd (这里需要修改) println!("File name: {}, fd: {}", *file.name, *file.fd); // 关闭文件并处理任何错误 match file.close() { Ok(()) => println!("File closed successfully"), Err(e) => println!("Error closing file: {}", e), } // 查抄关闭后的文件巨细 let metadata = metadata("test.txt").unwrap(); println!("File size: {} bytes", metadata.len()); }
复制代码 三种方案的选择
这三种方案的选择要根据实际环境,通常第二个方案。但是如果真的字段太多要写的unwrap太多的话就需要思量其他的方案。
如果代码充足简单,可以轻松查抄代码的安全性,那么第三种ManuallyDrop方案也是非常好的。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |