拆分`Vec`

Rod*_*ick 5 unsafe rust borrow-checker

我正在尝试编写一个缓冲区用于解析,所以我可以在解析它们时从前面拉出记录,理想情况下不会制作任何副本,只是在运行时将缓冲区前面的块的所有权转移掉.这是我的实现:

struct BufferThing {
    buf: Vec<u8>,
}

impl BufferThing {
    fn extract(&mut self, size: usize) -> Vec<u8> {
        assert!(size <= self.buf.len());
        let remaining: usize = self.buf.len() - size;
        let ptr: *mut u8 = self.buf.as_mut_ptr();

        unsafe {
            self.buf = Vec::from_raw_parts(ptr.offset(size as isize), remaining, remaining);
            Vec::from_raw_parts(ptr, size, size)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这会编译,但signal: 11, SIGSEGV: invalid memory reference在开始运行时会发生恐慌.这与Nomicon中的示例大致相同,但我试图在它上面做Vec,我试图分割一个字段而不是对象本身.

是否可以在不复制其中一个的情况下执行此操作Vec?Nomicon或其他文档中是否有一些部分可以解释为什么我要把所有内容都搞砸了unsafe

Mat*_* M. 5

不幸的是,这不是内存分配器的工作原理。在过去,当内存非常宝贵时,这可能是可能的,但今天的分配器是为了速度而不是内存保存。

内存分配器的常见实现是使用slab。基本上,它是:

struct Allocator {
    less_than_32_bytes: List<[u8; 32]>,
    less_than_64_bytes: List<[u8; 64]>,
    less_than_128_bytes: List<[u8; 128]>,
    less_than_256_bytes: List<[u8; 256]>,
    less_than_512_bytes: List<[u8; 512]>,
    ...
}
Run Code Online (Sandbox Code Playgroud)

当您请求 96 字节时,它会从 中获取一个元素less_than_128_bytes

当您释放该元素时,它会释放所有元素,而不仅仅是前 N 个字节,并且整个块现在可以重用。块内的任何指针现在都是悬空的,不应取消引用。

此外,尝试释放块中间的指针只会让分配器感到困惑:它找不到它,因为约定是您通过块的第一个字节来寻址块。

unsafe您使用代码BOOM违反了合同。


我提出的解决方案很简单:

  • Vec<u8>使用包含整个缓冲区的单个来解析
  • 使用切片进行Vec解析

Rust 将检查生命周期,因此您的切片不能比缓冲区存活得更久,并且进一步切片 ( s[..offset], s[offset..]) 不会分配。


如果您不介意一次分配,则可以为分割部分Vec::split_off分配一个足够大的新分配。Vec