喜欢的话别忘了点赞、收藏加关注哦(加关注即可阅读全文),对接下来的教程有兴趣的可以关注专栏。谢谢喵!(=・ω・=)
说句题外话,这篇文章一共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::os::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
方案也是非常好的。