我想将此函数更改为返回 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 的可变引用?
答案 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()
分支来混淆借用检查器。