Rust 设计存储结构和只读结构用户的方法

Jam*_*979 5 rust

tl;dr创建一些字节存储的最佳“Rust 方式”是什么,在这种情况下是 a Vec<u8>,将其存储Vec<u8>struct可以使用键值(如 a BTreeMap<usize, &Vec<u8>>)访问的字段中,然后Vec<u8>从其他一些structs 中读取它们?
      这是否可以推断为类似structs的一般良好的防锈设计,这些s 充当可使用键(偏移量、索引、文件路径等)访问的字节(Vec<u8>[u8; 16384]、 等)的存储和缓存?usizeu32String

目标

我正在尝试创建一个字节存储structimpl功能:

  1. 将按需从磁盘读取的 16384 字节存储到Vec<u8>容量为 16384 的“块”中
  2. 其他人struct将分析各种Vec<u8>并可能需要存储自己对这些“块”的引用
  3. 高效:在内存中只有一个“块”的副本,避免不必要的复制、克隆等。

不幸的是,对于每次实现尝试,我都会遇到借用、生命周期省略、可变性、复制或其他问题的难题。

简化代码示例

我创建了一个struct BlockReader

  1. 创建一个Vec<u8>( Vec<u8>::with_capacity(16384)) 类型为Block
  2. 从文件中读取(使用File::seekFile::take::read_to_end)并将 16384 存储u8到一个Vec<u8>
  3. 存储对Vec<u8>内部BTreeMap类型为的引用Blocks

游乐场代码

use std::io::Seek;
use std::io::SeekFrom;
use std::io::Read;
use std::fs::File;
use std::collections::BTreeMap;

type Block = Vec<u8>;
type Blocks<'a> = BTreeMap<usize, &'a Block>;

pub struct BlockReader<'a> {
    blocks: Blocks<'a>,
    file: File,
}

impl<'a> BlockReader<'a> {
    /// read a "block" of 16384 `u8` at file offset 
    /// `offset` which is multiple of 16384
    /// if the "block" at the `offset` is cached in
    /// `self.blocks` then return a reference to that
    /// XXX: assume `self.file` is already `open`ed file
    ///      handle
    fn readblock(& mut self, offset: usize) -> Result<&Block, std::io::Error> {
        // the data at this offset is the "cache"
        // return reference to that
        if self.blocks.contains_key(&offset) {
            return Ok(&self.blocks[&offset]);
        }
        // have not read data at this offset so read
        // the "block" of data from the file, store it,
        // return a reference
        let mut buffer = Block::with_capacity(16384);
        self.file.seek(SeekFrom::Start(offset as u64))?;
        self.file.read_to_end(&mut buffer);
        self.blocks.insert(offset, & buffer);
        Ok(&self.blocks[&offset])
    }
}
Run Code Online (Sandbox Code Playgroud)

用例问题示例

每个实现都有很多问题。例如,对BlockReader.readblocka 的两次调用struct BlockAnalyzer1就造成了无穷无尽的困难:

pub struct BlockAnalyzer1<'b> {
   pub blockreader: BlockReader<'b>,
}

impl<'b> BlockAnalyzer1<'b> {
    /// contrived example function
    pub fn doStuff(&mut self) -> Result<bool, std::io::Error> {
        let mut b: &Block;
        match self.blockreader.readblock(3 * 16384) {
            Ok(val) => {
                b = val;
            },
            Err(err) => {
                return Err(err);
            }
        }
        match self.blockreader.readblock(5 * 16384) {
            Ok(val) => {
                b = val;
            },
            Err(err) => {
                return Err(err);
            }
        }
        Ok(true)
    }
}
Run Code Online (Sandbox Code Playgroud)

结果是

error[E0597]: `buffer` does not live long enough
  --> src/lib.rs:34:36
   |
15 | impl<'a> BlockReader<'a> {
   |      -- lifetime `'a` defined here
...
34 |         self.blocks.insert(offset, & buffer);
   |         ---------------------------^^^^^^^^-
   |         |                          |
   |         |                          borrowed value does not live long enough
   |         argument requires that `buffer` is borrowed for `'a`
35 |         Ok(&self.blocks[&offset])
36 |     }
   |     - `buffer` dropped here while still borrowed
Run Code Online (Sandbox Code Playgroud)

但是,对于这种设计的不同排列,我遇到了许多其他错误,例如我遇到的另一个错误

error[E0499]: cannot borrow `self.blockreader` as mutable more than once at a time
   --> src/main.rs:543:23
    |
463 | impl<'a> BlockUser1<'a> {
    |      ----------- lifetime `'a` defined here
...
505 |             match self.blockreader.readblock(3 * 16384) {
    |                   ---------------------------------------
    |                   |
    |                   first mutable borrow occurs here
    |                   argument requires that `self.blockreader` is borrowed for `'a`
...
543 |                 match self.blockreader.readblock(5 * 16384) {
    |                       ^^^^^^^^^^^^^^^^ second mutable borrow occurs here
Run Code Online (Sandbox Code Playgroud)

在 中BlockReader,我尝试Block使用Vec<u8>, &Vec<u8>, Box<Vec<u8>>, Box<&Vec<u8>>, &Box<&Vec<u8>>,&Pin<&Box<&Vec<u8>>等对“ ”存储进行排列。但是,每个实现排列都会遇到各种与借用、生命周期和可变性有关的混淆问题。

同样,我不是在寻找特定的修复方法。我正在为这个一般问题寻找一种通常良好的面向 Rust 的设计方法:存储由 some 管理的一团字节struct,有其他struct获取对一团字节的引用(或指针等),在循环(同时可能存储新的字节块)。

Rust 专家的问题

Rust 专家将如何解决这个问题?
我应该如何将Vec<u8>( Block)存储在中BlockReader.blocks,并允许其他Struct人将他们自己的引用(或指针,或对指针的引用,或固定框指针等)存储到Block
其他人是否应该struct复制或克隆 aBox<Block>或 aPin<Box<Block>>或其他东西?
会使用不同的存储,比如固定大小的数组;type Block = [u8; 16384];更容易传递引用?
是否应该给出其他Structlike 、 or 、 or或其他东西?BlockUser1&BlockBox<Block>&Pin<&Box<&Block>

同样,每个Vec<u8>( Block) 被写入一次 (during BlockReader.readblock) 并且可以被其他Structs 通过调用读取多次BlockReader.readblock,然后通过保存他们自己的引用/指针/等。到那个Block(理想情况下,也许这不理想?)。

Mic*_*son 3

如果它们是不可变的,您可以将Vec<u8>anRc<RefCell<...>>或简单的 a放在后面。Rc<..>

如果您需要线程安全访问,则需要使用Arc<Mutex<...>>orArc<RwLock<...>>代替。

这是代码的转换版本。(有一些拼写错误和位需要更改才能编译 - 你应该真正修复示例中的这些错误,并给我们一些几乎可以编译的东西......)你也可以在操场上看到这一点

use std::io::Seek;
use std::io::SeekFrom;
use std::io::Read;
use std::fs::File;
use std::cell::RefCell;
use std::rc::Rc;
use std::collections::BTreeMap;

type Block = Vec<u8>;
type Blocks = BTreeMap<usize, Rc<RefCell<Block>>>;

pub struct BlockReader {
    blocks: Blocks,
    file: File,
}

impl BlockReader {
    /// read a "block" of 16384 `u8` at file offset 
    /// `offset` which is multiple of 16384
    /// if the "block" at the `offset` is cached in
    /// `self.blocks` then return a reference to that
    /// XXX: assume `self.file` is already `open`ed file
    ///      handle
    fn readblock(& mut self, offset: usize) -> Result<Rc<RefCell<Block>>,std::io::Error> {
        // the data at this offset is the "cache"
        // return reference to that
        if self.blocks.contains_key(&offset) {
            return Ok(self.blocks[&offset].clone());
        }
        // have not read data at this offset so read
        // the "block" of data from the file, store it,
        // return a reference
        let mut buffer = Block::with_capacity(16384);
        self.file.seek(SeekFrom::Start(offset as u64))?;
        self.file.read_to_end(&mut buffer);
        self.blocks.insert(offset, Rc::new(RefCell::new(buffer)));
        Ok(self.blocks[&offset].clone())
    }
}

pub struct BlockAnalyzer1 {
   pub blockreader: BlockReader,
}

impl BlockAnalyzer1 {
    /// contrived example function
    pub fn doStuff(&mut self) -> Result<bool,std::io::Error> {
        let mut b: Rc<RefCell<Block>>;
        match self.blockreader.readblock(3 * 16384) {
            Ok(val) => {
                b = val;
            },
            Err(err) => {
                return Err(err);
            }
        }
        match self.blockreader.readblock(5 * 16384) {
            Ok(val) => {
                b = val;
            },
            Err(err) => {
                return Err(err);
            }
        }
        Ok(true)
    }
}
Run Code Online (Sandbox Code Playgroud)