Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
160 views
in Technique[技术] by (71.8m points)

Rust: generic must be implement &xx[...]

I'm trying to implement a generic struct where it's clear from the beginnging that it's element 'provider' must be of a type that supports &myvar.provider[..] later on. But I'm unable to figure the correct Bound for this.

pub struct MyStruct<T: ??> {  // T must support &x.provider[..]
   pub provider: T,
}

Thanks a lot for your help

UPDATE: extended example. What I'm trying to achieve: data blocks can be up to 10GB. They can be provided as static b"aa" mostly for testing, read the file content into memory, or mmap the file content. No matter the provider, when processing the data we only use &[u8].

//
// A minimal example for my problem
//

use std::str;
use std::error::Error;
use std::fs::File;
use std::io::prelude::*;
use std::boxed::Box;
use memmap::{MmapOptions, Mmap};

#[derive(Debug)]
pub struct DBFileBuilder<T> {  // what is the correct bound? T must support &x[..]
    pub filename: String,
    pub provider: Option<T>,
    pub startpos: usize,
    // ... several more 
}

pub struct DBFile<'a, T> {  // what is the correct bound? T must support &x[..]
    pub filename: String,
    pub provider: T,
    pub data: &'a [u8],
    // ... several more 
}

impl<T> DBFileBuilder<T> {
    fn default() -> Self {
        Self { 
            filename: String::default(), 
            provider: None,
            startpos: 0,
        }
    }

    pub fn from_bytes(data: &[u8]) -> DBFileBuilder<&[u8]> {
        DBFileBuilder { 
            provider: Some(&data),
            ..DBFileBuilder::default() 
        }
    }

    pub fn read_file(filename: &str) -> Result<DBFileBuilder<Box<[u8]>>, Box<dyn Error>> {
        let mut file = File::open(&filename)?;
        let fsize = file.metadata()?.len();

        let mut provider = vec![0_u8; fsize as usize].into_boxed_slice();
        let n = file.read(&mut provider)?;

        Ok(DBFileBuilder {
            filename: filename.to_string(),
            provider: Some(provider),
            ..DBFileBuilder::default()
        })
    }

    pub fn mmap_file(filename: &str) -> Result<DBFileBuilder<Mmap>, Box<dyn Error>> {
        let file = File::open(&filename)?;
        let provider = unsafe { MmapOptions::new().map(&file)? };

        Ok(DBFileBuilder {
            filename: filename.to_string(),
            provider: Some(provider),
            ..DBFileBuilder::default()
        })
    }

    pub fn init(&mut self) {
    }

    pub fn build<'a>(self) -> DBFile<'a, T> {
        let provider = self.provider.expect("Provider not initialized");
        self.init();
        let data = &provider[self.startpos ..];

        DBFile {
            filename: self.filename,
            provider,
            data,
        }
    }
}

impl<'a, T> DBFile<'a, T> {
    pub fn run(&self) { 
        return self.process(self.data)
    } 

    pub fn process(&self, data: &[u8]) { 
        println!("data: {:?}", &data); 
    }
}
question from:https://stackoverflow.com/questions/65853124/rust-generic-must-be-implement-xx

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The necessary trait bound to index by usize closed Ranges to get bytes is Index<Range<usize>, Output=[u8]>. You'll probably also want to index by infinite ranges:

pub struct MyStruct<T>
where T: Index<Range<usize>, Output=[u8]>
   + Index<RangeTo<usize>, Output=[u8]>
   + Index<RangeFrom<usize>, Output=[u8]>
   + Index<RangeFull, Output=[u8]>
{
    pub provider: T,
}

Unforunately, these bounds currently aren't inferred in signatures where MyStruct is used (although I believe this is in the works), which can get very unwieldy. If you use Nightly, you can define a trait alias (RFC1733) to make this less verbose.

That said, you'll need to rethink the example as it wouldn't work: E.g. in build() the data can't be part of the returned DBFile because it points to a local variable which doesn't live long enough.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...