Rust:return 来自函数的通用结构,其中(仅)<T> 不同
Rust: return a generic struct from a function where (only) <T> is different
我正在寻求有关正确语法或 Rust 方法的帮助。我的用例:我有一个通用结构 FileData
,它有一个名为 provider
的变量。提供者必须实现 AsRef<[u8]>
以便数据可能来自静态字节、堆分配内存、内存映射以及可能的其他方式。我有几种创建 FileData
的方法,它们似乎运行良好。但是有一个
// ERROR: This is the line that I do not get right
pub fn from_file<P: AsRef<Path>>(filename: P, mmap: bool) -> Result<FileData<T>, Box<dyn Error>> {
if mmap == true {
return FileData::mmap_file(filename)
} else {
return FileData::read_file(filename)
}
}
我没听清。该方法总是returns一个FileData,返回取决于'mmap'参数,<T>
是不同的。它可以是 <Box<[u8]>
或 <Mmap>
.
我搜索了类似的问题和文章,但找到了符合我情况的,例如(1), (2), (3).
#[derive(Debug)]
pub struct FileData<T: AsRef<[u8]>> {
pub filename: String,
pub provider: T, // data block, file read, mmap, and potentially more
pub fsize: u64,
pub mmap: bool,
}
impl FileData<&[u8]> {
/// Useful for testing. Create a FileData builder based on some bytes.
#[allow(dead_code)]
pub fn from_bytes(data: &'static [u8]) -> Self {
FileData {
filename: String::new(),
provider: data,
fsize: data.len() as _,
mmap: false,
}
}
}
pub fn path_to_string<P: AsRef<Path>>(filename: P) -> String {
return String::from(filename.as_ref().to_str().unwrap_or_default());
}
pub fn file_size(file: &File) -> Result<u64, Box<dyn Error>> {
Ok(file.metadata()?.len())
}
impl FileData<Box<[u8]>> {
/// Read the full file content into memory, which will be allocated on the heap.
#[allow(dead_code)]
pub fn read_file<P: AsRef<Path>>(filename: P) -> Result<Self, Box<dyn Error>> {
let mut file = File::open(&filename)?;
let fsize = file_size(&file)?;
let mut provider = vec![0_u8; fsize as usize].into_boxed_slice();
let n = file.read(&mut provider)? as u64;
assert!(fsize == n, "Failed to read all data from file: {} vs {}", n, fsize);
Ok(FileData {
filename: path_to_string(&filename),
provider: provider,
fsize: fsize,
mmap: false,
})
}
}
impl FileData<Mmap> {
/// Memory Map the file content
#[allow(dead_code)]
pub fn mmap_file<P: AsRef<Path>>(filename: P) -> Result<Self, Box<dyn Error>> {
let file = File::open(&filename)?;
let fsize = file_size(&file)?;
let provider = unsafe { MmapOptions::new().map(&file)? };
Ok(FileData {
filename: path_to_string(&filename),
provider: provider,
fsize: fsize,
mmap: true,
})
}
}
impl<T: AsRef<[u8]>> FileData<T> {
#[allow(dead_code)]
pub fn from_file<P: AsRef<Path>>(filename: P, mmap: bool) -> Result<FileData<_>, Box<dyn Error>> {
if mmap == true {
return FileData::mmap_file(filename)
} else {
return FileData::read_file(filename)
}
}
pub fn as_ref(&self) -> &[u8] {
return self.provider.as_ref()
}
}
错误信息是:
error[E0308]: mismatched types
--> src\data_files\file_data.rs:87:20
|
83 | impl<T: AsRef<[u8]>> FileData<T> {
| - this type parameter
84 | #[allow(dead_code)]
85 | pub fn from_file<P: AsRef<Path>>(filename: P, mmap: bool) -> Result<FileData<T>, Box<dyn Error>> {
| ----------------------------------- expected `std::result::Result<file_data::FileData<T>,
std::boxed::Box<(dyn std::error::Error + 'static)>>` because of return type
86 | if mmap == true {
87 | return FileData::mmap_file(filename)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected type parameter `T`, found struct `Mmap`
|
= note: expected enum `std::result::Result<file_data::FileData<T>, _>`
found enum `std::result::Result<file_data::FileData<Mmap>, _>`
泛型赋予调用者决定函数的return类型的权利。现在您的函数 被调用者 正在决定 return 类型,这就是您遇到编译器错误的原因。
您可以重构代码,通过实现附加特征 IntoFileData
,然后将其添加为绑定到您的通用 FileData<T>
实现的特征,将权利交还给调用者。简化的注释示例:
use memmap::Mmap;
use memmap::MmapOptions;
use std::error::Error;
use std::fs::File;
use std::io::Read;
use std::path::Path;
// simplified FileData for brevity
struct FileData<T: AsRef<[u8]>> {
provider: T,
}
// new trait for converting types into FileData
trait IntoFileData<T: AsRef<[u8]>> {
fn from_path(path: &Path) -> Result<FileData<T>, Box<dyn Error>>;
}
impl IntoFileData<Box<[u8]>> for Box<[u8]> {
fn from_path(path: &Path) -> Result<FileData<Box<[u8]>>, Box<dyn Error>> {
let mut file = File::open(path)?;
let size = file.metadata()?.len();
let mut provider = vec![0_u8; size as usize].into_boxed_slice();
let read = file.read(&mut provider)? as u64;
assert!(
size == read,
"Failed to read all data from file: {} vs {}",
read,
size
);
Ok(FileData { provider })
}
}
impl IntoFileData<Mmap> for Mmap {
fn from_path(path: &Path) -> Result<FileData<Mmap>, Box<dyn Error>> {
let file = File::open(path)?;
let provider = unsafe { MmapOptions::new().map(&file)? };
Ok(FileData { provider })
}
}
// this signature gives the caller the right to choose the type of FileData
impl<T: AsRef<[u8]> + IntoFileData<T>> FileData<T> {
fn from_path(path: &Path) -> Result<FileData<T>, Box<dyn Error>> {
T::from_path(path)
}
}
fn example(path: &Path) {
// caller asks for and gets file data as Box<[u8]>
let file_data: FileData<Box<[u8]>> = FileData::from_path(path).unwrap();
// caller asks for and gets file data as Mmap
let file_data: FileData<Mmap> = FileData::from_path(path).unwrap();
}
如果您想赋予被调用者决定 return 类型的权利,您必须 return 一个特征对象。简化的注释示例:
use memmap::Mmap;
use memmap::MmapOptions;
use std::error::Error;
use std::fs::File;
use std::io::Read;
use std::path::Path;
// simplified FileData for brevity
struct FileData {
provider: Box<dyn AsRef<[u8]>>,
}
fn vec_from_path(path: &Path) -> Result<FileData, Box<dyn Error>> {
let mut file = File::open(path)?;
let size = file.metadata()?.len();
let mut provider = vec![0_u8; size as usize];
let read = file.read(&mut provider)? as u64;
assert!(
size == read,
"Failed to read all data from file: {} vs {}",
read,
size
);
Ok(FileData {
provider: Box::new(provider),
})
}
fn mmap_from_path(path: &Path) -> Result<FileData, Box<dyn Error>> {
let file = File::open(path)?;
let provider = unsafe { MmapOptions::new().map(&file)? };
Ok(FileData {
provider: Box::new(provider),
})
}
impl FileData {
fn from_path(path: &Path, mmap: bool) -> Result<FileData, Box<dyn Error>> {
if mmap {
mmap_from_path(path)
} else {
vec_from_path(path)
}
}
}
fn example(path: &Path) {
// file data could be vec or mmap, callee decides
let file_data = FileData::from_path(path, true).unwrap();
let file_data = FileData::from_path(path, false).unwrap();
}
我正在寻求有关正确语法或 Rust 方法的帮助。我的用例:我有一个通用结构 FileData
,它有一个名为 provider
的变量。提供者必须实现 AsRef<[u8]>
以便数据可能来自静态字节、堆分配内存、内存映射以及可能的其他方式。我有几种创建 FileData
的方法,它们似乎运行良好。但是有一个
// ERROR: This is the line that I do not get right
pub fn from_file<P: AsRef<Path>>(filename: P, mmap: bool) -> Result<FileData<T>, Box<dyn Error>> {
if mmap == true {
return FileData::mmap_file(filename)
} else {
return FileData::read_file(filename)
}
}
我没听清。该方法总是returns一个FileData,返回取决于'mmap'参数,<T>
是不同的。它可以是 <Box<[u8]>
或 <Mmap>
.
我搜索了类似的问题和文章,但找到了符合我情况的,例如(1), (2), (3).
#[derive(Debug)]
pub struct FileData<T: AsRef<[u8]>> {
pub filename: String,
pub provider: T, // data block, file read, mmap, and potentially more
pub fsize: u64,
pub mmap: bool,
}
impl FileData<&[u8]> {
/// Useful for testing. Create a FileData builder based on some bytes.
#[allow(dead_code)]
pub fn from_bytes(data: &'static [u8]) -> Self {
FileData {
filename: String::new(),
provider: data,
fsize: data.len() as _,
mmap: false,
}
}
}
pub fn path_to_string<P: AsRef<Path>>(filename: P) -> String {
return String::from(filename.as_ref().to_str().unwrap_or_default());
}
pub fn file_size(file: &File) -> Result<u64, Box<dyn Error>> {
Ok(file.metadata()?.len())
}
impl FileData<Box<[u8]>> {
/// Read the full file content into memory, which will be allocated on the heap.
#[allow(dead_code)]
pub fn read_file<P: AsRef<Path>>(filename: P) -> Result<Self, Box<dyn Error>> {
let mut file = File::open(&filename)?;
let fsize = file_size(&file)?;
let mut provider = vec![0_u8; fsize as usize].into_boxed_slice();
let n = file.read(&mut provider)? as u64;
assert!(fsize == n, "Failed to read all data from file: {} vs {}", n, fsize);
Ok(FileData {
filename: path_to_string(&filename),
provider: provider,
fsize: fsize,
mmap: false,
})
}
}
impl FileData<Mmap> {
/// Memory Map the file content
#[allow(dead_code)]
pub fn mmap_file<P: AsRef<Path>>(filename: P) -> Result<Self, Box<dyn Error>> {
let file = File::open(&filename)?;
let fsize = file_size(&file)?;
let provider = unsafe { MmapOptions::new().map(&file)? };
Ok(FileData {
filename: path_to_string(&filename),
provider: provider,
fsize: fsize,
mmap: true,
})
}
}
impl<T: AsRef<[u8]>> FileData<T> {
#[allow(dead_code)]
pub fn from_file<P: AsRef<Path>>(filename: P, mmap: bool) -> Result<FileData<_>, Box<dyn Error>> {
if mmap == true {
return FileData::mmap_file(filename)
} else {
return FileData::read_file(filename)
}
}
pub fn as_ref(&self) -> &[u8] {
return self.provider.as_ref()
}
}
错误信息是:
error[E0308]: mismatched types
--> src\data_files\file_data.rs:87:20
|
83 | impl<T: AsRef<[u8]>> FileData<T> {
| - this type parameter
84 | #[allow(dead_code)]
85 | pub fn from_file<P: AsRef<Path>>(filename: P, mmap: bool) -> Result<FileData<T>, Box<dyn Error>> {
| ----------------------------------- expected `std::result::Result<file_data::FileData<T>,
std::boxed::Box<(dyn std::error::Error + 'static)>>` because of return type
86 | if mmap == true {
87 | return FileData::mmap_file(filename)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected type parameter `T`, found struct `Mmap`
|
= note: expected enum `std::result::Result<file_data::FileData<T>, _>`
found enum `std::result::Result<file_data::FileData<Mmap>, _>`
泛型赋予调用者决定函数的return类型的权利。现在您的函数 被调用者 正在决定 return 类型,这就是您遇到编译器错误的原因。
您可以重构代码,通过实现附加特征 IntoFileData
,然后将其添加为绑定到您的通用 FileData<T>
实现的特征,将权利交还给调用者。简化的注释示例:
use memmap::Mmap;
use memmap::MmapOptions;
use std::error::Error;
use std::fs::File;
use std::io::Read;
use std::path::Path;
// simplified FileData for brevity
struct FileData<T: AsRef<[u8]>> {
provider: T,
}
// new trait for converting types into FileData
trait IntoFileData<T: AsRef<[u8]>> {
fn from_path(path: &Path) -> Result<FileData<T>, Box<dyn Error>>;
}
impl IntoFileData<Box<[u8]>> for Box<[u8]> {
fn from_path(path: &Path) -> Result<FileData<Box<[u8]>>, Box<dyn Error>> {
let mut file = File::open(path)?;
let size = file.metadata()?.len();
let mut provider = vec![0_u8; size as usize].into_boxed_slice();
let read = file.read(&mut provider)? as u64;
assert!(
size == read,
"Failed to read all data from file: {} vs {}",
read,
size
);
Ok(FileData { provider })
}
}
impl IntoFileData<Mmap> for Mmap {
fn from_path(path: &Path) -> Result<FileData<Mmap>, Box<dyn Error>> {
let file = File::open(path)?;
let provider = unsafe { MmapOptions::new().map(&file)? };
Ok(FileData { provider })
}
}
// this signature gives the caller the right to choose the type of FileData
impl<T: AsRef<[u8]> + IntoFileData<T>> FileData<T> {
fn from_path(path: &Path) -> Result<FileData<T>, Box<dyn Error>> {
T::from_path(path)
}
}
fn example(path: &Path) {
// caller asks for and gets file data as Box<[u8]>
let file_data: FileData<Box<[u8]>> = FileData::from_path(path).unwrap();
// caller asks for and gets file data as Mmap
let file_data: FileData<Mmap> = FileData::from_path(path).unwrap();
}
如果您想赋予被调用者决定 return 类型的权利,您必须 return 一个特征对象。简化的注释示例:
use memmap::Mmap;
use memmap::MmapOptions;
use std::error::Error;
use std::fs::File;
use std::io::Read;
use std::path::Path;
// simplified FileData for brevity
struct FileData {
provider: Box<dyn AsRef<[u8]>>,
}
fn vec_from_path(path: &Path) -> Result<FileData, Box<dyn Error>> {
let mut file = File::open(path)?;
let size = file.metadata()?.len();
let mut provider = vec![0_u8; size as usize];
let read = file.read(&mut provider)? as u64;
assert!(
size == read,
"Failed to read all data from file: {} vs {}",
read,
size
);
Ok(FileData {
provider: Box::new(provider),
})
}
fn mmap_from_path(path: &Path) -> Result<FileData, Box<dyn Error>> {
let file = File::open(path)?;
let provider = unsafe { MmapOptions::new().map(&file)? };
Ok(FileData {
provider: Box::new(provider),
})
}
impl FileData {
fn from_path(path: &Path, mmap: bool) -> Result<FileData, Box<dyn Error>> {
if mmap {
mmap_from_path(path)
} else {
vec_from_path(path)
}
}
}
fn example(path: &Path) {
// file data could be vec or mmap, callee decides
let file_data = FileData::from_path(path, true).unwrap();
let file_data = FileData::from_path(path, false).unwrap();
}