簡體   English   中英

Rust:從 function 返回一個通用結構,其中(僅)<t> 是不同的</t>

[英]Rust: return a generic struct from a function where (only) <T> is different

我正在尋求有關正確語法或 Rust 方法的幫助。 我的用例:我有一個通用的 struct FileData ,它有一個名為provider的變量。 提供者必須實現AsRef<[u8]>以便數據可能來自 static 字節、堆分配的 memory、memory 映射,以及可能的其他數據。 我有幾種創建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)
    }
}

我不明白。 該方法總是返回一個 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>, _>`

Generics 賦予調用者決定 function 的返回類型應該是什么的權利。 現在你的 function ,被調用者,正在決定返回類型,這就是你得到編譯器錯誤的原因。

您可以通過實現附加特征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();
}

操場


如果你想賦予被調用者決定返回類型的權利,你必須返回一個特征 object。 簡化注釋示例:

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();
}

操場

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM