【Rust中级教程】2.8. API设计原则之灵活性(flexible) Pt.4:显式析构函数的问题及3种解决方案
喜欢的话别忘了点赞、收藏加关注哦(加关注即可阅读全文),对接下来的教程有兴趣的可以关注专栏。谢谢喵!(=・ω・=)

说句题外话,这篇文章一共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方案也是非常好的。
相关文章:
【Rust中级教程】2.8. API设计原则之灵活性(flexible) Pt.4:显式析构函数的问题及3种解决方案
喜欢的话别忘了点赞、收藏加关注哦(加关注即可阅读全文),对接下来的教程有兴趣的可以关注专栏。谢谢喵!(・ω・) 说句题外话,这篇文章一共5721个字,是我截至目前写的最长的一篇文章&a…...
LabVIEW Browser.vi 库说明
browser.llb 库位于C:\Program Files (x86)\National Instruments\LabVIEW 2019\vi.lib\Platform目录,它是 LabVIEW 平台下用于与网络浏览器相关操作的重要库。该库为 LabVIEW 开发者提供了一系列工具,用于实现网页浏览控制、网页数据获取与交互等功能&a…...
promise的方法有哪些?【JavaScript】
Promise对象在JavaScript中是一种处理异步操作的方式,它提供了一组方法来管理和控制异步操作的结果。以下是一些常用的Promise方法: 以下是对 constructor(executor)、then(onFulfilled, onRejected)、catch(onRejected)、 finally(onFin…...
基于模仿学习(IL)的端到端自动驾驶发展路径
基于模仿学习(IL)的端到端自动驾驶发展路径 1. 核心论文解析 (1) UniAD:感知-规划一体化 核心思想:首次提出将感知任务(如目标检测、车道线识别、轨迹预测)与规划任务集成到统一的端到端框架中ÿ…...
第1篇:SOLR 简介与源码环境搭建
第1篇:SOLR 简介与源码环境搭建 1.1 SOLR 是什么? Apache SOLR 是一个基于 Apache Lucene 的高性能开源搜索平台。它不仅继承了 Lucene 强大的全文搜索能力,还通过封装和扩展,提供了企业级的功能,比如分布式搜索(SolrCloud)、RESTful API、动态 Schema 管理等。自 200…...
Docker 搭建 Redis 数据库
Docker 搭建 Redis 数据库 前言一、准备工作二、创建 Redis 容器的目录结构三、启动 Redis 容器1. 通过 redis.conf 配置文件设置密码2. 通过 Docker 命令中的 requirepass 参数设置密码 四、Host 网络模式与 Port 映射模式五、检查 Redis 容器状态六、访问 Redis 服务总结 前言…...
MySQL 连表查询:原理、语法与优化
目录 引言 什么是连表查询? 连表查询的类型 1. 内连接(INNER JOIN) 2. 左连接(LEFT JOIN) 3. 右连接(RIGHT JOIN) 4. 全连接(FULL JOIN) 5. 交叉连接(…...
实战技巧:如何快速提高网站收录的权威性?
快速提高网站收录的权威性是一个系统性的工作,涉及内容质量、网站结构、外部链接、用户体验等多个方面。以下是一些实战技巧,可以帮助你快速提升网站收录的权威性: 一、提升内容质量 原创性: 确保网站内容具备高质量与原创性&a…...
vue语法v-model例子单选题和多选题
<template><!-- 单选框 --><input type"radio" v-model"danxuan" value"a"><label for"a">a</label><input type"radio" v-model"danxuan" value"b"><label fo…...
计算机网络面试知识点总结
目录 1. 计算机网络的基本知识点2. OSI 七层模型3. TCP/IP 四层模型4. TCP 和 UDP4.1 TCP 协议4.2 TCP 流量控制4.3 TCP 拥塞控制4.4 TCP 三次握手4.5 TCP 四次挥手4.6 TCP 粘包问题4.7 TCP Socket交互流程4.8 UDP 协议以及和 TCP 协议的不同 5. HTTP协议5.1 HTTP 请求方法以及…...
JVM生产环境问题定位与解决实战(二):JConsole、VisualVM到MAT的高级应用
生产问题定位指南:几款必备的可视化工具 引言 在上一篇文章中,详细的介绍了JDK自带的一系列命令行工具,,如jps、jmap、jstat、jstack以及jcmd等,这些工具为排查和诊断Java虚拟机(JVM)问题提供…...
c++入门-------命名空间、缺省参数、函数重载
C系列 文章目录 C系列前言一、命名空间二、缺省参数2.1、缺省参数概念2.2、 缺省参数分类2.2.1、全缺省参数2.2.2、半缺省参数 2.3、缺省参数的特点 三、函数重载3.1、函数重载概念3.2、构成函数重载的条件3.2.1、参数类型不同3.2.2、参数个数不同3.2.3、参数类型顺序不同 前言…...
Lua语言入门(自用)
全局与非全局 在lua语言当中没有被local表示的是全局变量 反之则是本地变量(仅仅作用在某个文件,函数,或者代码块) 下面是实例代码和运行结果 --hello.luaA 10;--这样就是全局变量,然后这个编译器如果是大写就是默认的全局变量 local b 3;--这样就是局部变量--reference.…...
大数据开发平台的框架
根据你的需求,以下是从 GitHub 推荐的 10 个可以实现大数据开发平台的项目: 1. Apache Spark Apache Spark 是一个开源的分布式计算框架,适用于大规模数据处理和分析。它提供了强大的数据处理能力,支持实时数据处理、机器学习和…...
X86_64位下的GS寄存器
在 64 位模式下,段机制通常(但不是完全)被禁用,从而创建一个平坦的 64 位线性地址空间。处理器将 CS、DS、ES、SS 的段基地址视为零,从而创建一个与有效地址相等的线性地址。FS 和 GS 段是例外。这些段寄存器ÿ…...
#6【TIP2023】Dif-Fusion
📜 Dif-Fusion: Toward High Color Fidelity in Infrared and Visible Image Fusion With Diffusion Models 🍸 源码:https://github.com/GeoVectorMatrix/Dif-Fusion 🍺 前向扩散过程 前向过程看成是逐步加噪的Markov链,在每一个时间步 t t t ,...
科普:“git“与“github“
Git与GitHub的关系可以理解为:Git是一种软件工具,而GitHub则是一个在线平台,它们是“一家子”。二者的关联最直接体现在你通过Git在GitHub仓库中clone软件包到你的机器中来。 具体来说: 一、Git 定义:Git是一个开源的…...
unplugin-vue-components,unplugin-auto-import,借用ai去学习
好的,我现在需要帮助用户理解他们提供的这段Vite配置代码。用户之前已经询问过关于ElementPlusResolver的导入问题,现在他们展示了在vite.config.ts中使用该解析器的具体配置。我需要逐步分析这段代码,并解释每个部分的作用。 首先ÿ…...
蓝桥杯 2013 省 B 翻硬币
题目背景 小明正在玩一个“翻硬币”的游戏。 题目描述 桌上放着排成一排的若干硬币。我们用 * 表示正面,用 o 表示反面(是小写字母,不是零),比如可能情形是 **oo***oooo,如果同时翻转左边的两个硬币&…...
【硬件设计】DDR3、DDR4、DDR5、DDR6性能对比与硬件设计要点
目录 一、各代DDR技术核心性能指标对比 二、各代DDR技术特性详解 三、硬件设计通用原则与差异化需求 四、技术演进趋势总结 一、各代DDR技术核心性能指标对比 指标DDR3DDR4DDR5DDR6(预测)发布时间2007年2014年2020年预计2026年5传输速率800-1600 MT…...
日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻
在如今就业市场竞争日益激烈的背景下,越来越多的求职者将目光投向了日本及中日双语岗位。但是,一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧?面对生疏的日语交流环境,即便提前恶补了…...
Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)
概述 在 Swift 开发语言中,各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过,在涉及到多个子类派生于基类进行多态模拟的场景下,…...
可靠性+灵活性:电力载波技术在楼宇自控中的核心价值
可靠性灵活性:电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中,电力载波技术(PLC)凭借其独特的优势,正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据,无需额外布…...
(二)TensorRT-LLM | 模型导出(v0.20.0rc3)
0. 概述 上一节 对安装和使用有个基本介绍。根据这个 issue 的描述,后续 TensorRT-LLM 团队可能更专注于更新和维护 pytorch backend。但 tensorrt backend 作为先前一直开发的工作,其中包含了大量可以学习的地方。本文主要看看它导出模型的部分&#x…...
理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端
🌟 什么是 MCP? 模型控制协议 (MCP) 是一种创新的协议,旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议,它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...
《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》
在注意力分散、内容高度同质化的时代,情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现,消费者对内容的“有感”程度,正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中࿰…...
如何更改默认 Crontab 编辑器 ?
在 Linux 领域中,crontab 是您可能经常遇到的一个术语。这个实用程序在类 unix 操作系统上可用,用于调度在预定义时间和间隔自动执行的任务。这对管理员和高级用户非常有益,允许他们自动执行各种系统任务。 编辑 Crontab 文件通常使用文本编…...
安卓基础(Java 和 Gradle 版本)
1. 设置项目的 JDK 版本 方法1:通过 Project Structure File → Project Structure... (或按 CtrlAltShiftS) 左侧选择 SDK Location 在 Gradle Settings 部分,设置 Gradle JDK 方法2:通过 Settings File → Settings... (或 CtrlAltS)…...
uni-app学习笔记三十五--扩展组件的安装和使用
由于内置组件不能满足日常开发需要,uniapp官方也提供了众多的扩展组件供我们使用。由于不是内置组件,需要安装才能使用。 一、安装扩展插件 安装方法: 1.访问uniapp官方文档组件部分:组件使用的入门教程 | uni-app官网 点击左侧…...
向量几何的二元性:叉乘模长与内积投影的深层联系
在数学与物理的空间世界中,向量运算构成了理解几何结构的基石。叉乘(外积)与点积(内积)作为向量代数的两大支柱,表面上呈现出截然不同的几何意义与代数形式,却在深层次上揭示了向量间相互作用的…...
