返回 Result<Cow<[u8]>> 而不是 Result<Vec<u8>>

时间:2021-07-27 09:18:52

标签: rust file-io

我想将此函数更改为返回 Result<Cow<[u8]>>,以避免将整个文件不必要地复制到内存中。

pub(crate) fn get_reader_bytes<R: Read + MmapBytesReader>(reader: &mut R) -> Result<Vec<u8>> {
    // we have a file so we can mmap
    if let Some(file) = reader.to_file() {
        let mmap = unsafe { memmap::Mmap::map(file)? };
        Ok(mmap[..].to_vec())
    } else {
        // we can get the bytes for free
        if let Some(bytes) = reader.to_bytes() {
            Ok(bytes.to_vec())
        } else {
            // we have to read to an owned buffer to get the bytes.
            let mut bytes = Vec::with_capacity(1024 * 128);
            reader.read_to_end(&mut bytes)?;
            if !bytes.is_empty()
                && (bytes[bytes.len() - 1] != b'\n' || bytes[bytes.len() - 1] != b'\r')
            {
                bytes.push(b'\n')
            }
            Ok(bytes)
        }
    }
}

我尝试了下面的代码,但得到了 error[E0515]: cannot return value referencing local data *mmap,这是有道理的。

pub(crate) fn get_reader_bytes<R: Read + MmapBytesReader>(reader: &mut R) -> Result<Cow<[u8]>>     {
      // we have a file so we can mmap
      if let Some(file) = reader.to_file() {
          let mmap = unsafe { memmap::Mmap::map(file)? };
          Ok(Cow::Borrowed(&mmap[..]))
      } else {
          // we can get the bytes for free
          if let Some(bytes) = (*reader).to_bytes() {
              Ok(Cow::Borrowed(bytes))
          } else {
              // we have to read to an owned buffer to get the bytes.
              let mut bytes = Vec::with_capacity(1024 * 128);
              reader.read_to_end(&mut bytes)?;
              if !bytes.is_empty()
                  && (bytes[bytes.len() - 1] != b'\n' || bytes[bytes.len() - 1] != b'\r')
              {
                  bytes.push(b'\n')
              }
              Ok(Cow::Owned(bytes))
          }
      }
  }

我不确定如何继续,我是否需要在调用函数之前创建 mmap 并将其作为可变引用传递?或者一个可变引用的选项?还是 Option 的可变引用?

1 个答案:

答案 0 :(得分:2)

      if let Some(file) = reader.to_file() {
          let mmap = unsafe { memmap::Mmap::map(file)? };
          Ok(Cow::Borrowed(&mmap[..]))

问题从这里开始:您正在创建一个新的 Mmap它负责内存映射的存在, 作为局部变量。因此,Mmap 在函数末尾被删除,由于内存映射不再存在,引用无效。

可以做的最接近的事情是返回 Mmap 本身——或者更确切地说,在这种情况下,返回您自己设计的枚举,而不是 {{1} },然后可以提供借用其任何变体的逻辑(就像 Cow 一样):

Cow

然后在 enum MyBytes<'a> { Borrowed(&'a [u8]), Owned(Vec<u8>), Mapped(memmap::Mmap), } impl std::ops::Deref for MyBytes<'_> { type Target = [u8]; fn deref(&self) -> &[u8] { match self { Self::Borrowed(ref_bytes) => ref_bytes, Self::Owned(vec) => &vec, Self::Mapped(mmap) => &mmap, } } } 中使用该枚举:

get_reader_bytes

注意 Borrowed 情况下的尴尬:我不得不调用 pub(crate) fn get_reader_bytes<R: Read + MmapBytesReader>(reader: &mut R) -> Result<MyBytes<'_>, std::io::Error> { // we have a file so we can mmap if let Some(file) = reader.to_file() { let mmap = unsafe { memmap::Mmap::map(file)? }; Ok(MyBytes::Mapped(mmap)) } else { // we can get the bytes for free if reader.to_bytes().is_some() { Ok(MyBytes::Borrowed(reader.to_bytes().unwrap())) } else { // we have to read to an owned buffer to get the bytes. let mut bytes = Vec::with_capacity(1024 * 128); reader.read_to_end(&mut bytes)?; if !bytes.is_empty() && (bytes[bytes.len() - 1] != b'\n' || bytes[bytes.len() - 1] != b'\r') { bytes.push(b'\n') } Ok(MyBytes::Owned(bytes)) } } } 两次。这是因为借用检查器当前不支持您使用 可变 引用执行某些操作,然后返回依赖于它的借用或删除该借用并执行其他操作的模式 - 它假定借用无条件地扩展到函数的末尾,防止您对可变引用执行任何其他操作。因此,在这种情况下,我们必须将检查字节切片是否可用与返回此类切片的操作分开。

对此更好的解决方案是将读取逻辑放在 MmapBytesReader trait 实现中(可能以 impls 调用的单独函数的形式,因此多个 impls 可以共享代码),所以根本没有 to_bytes() 分支来混淆借用检查器。

相关问题