拆分`Vec`

时间:2017-02-10 16:18:43

标签: rust unsafe 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)
        }
    }
}

这会编译,但在开始运行时会出现signal: 11, SIGSEGV: invalid memory reference的恐慌。这与the Nomicon中的示例大致相同,但我试图在Vec上进行,我正在尝试拆分字段而不是对象本身。

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

1 个答案:

答案 0 :(得分:3)

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

内存分配器的常见实现是使用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]>,
    ...
}

当您请求96个字节时,它需要less_than_128_bytes中的元素。

释放该元素时,它会释放所有,而不仅仅是前N个字节,整个块现在可以重复使用。块内的任何指针现在悬空,不应该被解除引用。

此外,尝试释放块中间的指针只会混淆分配器:它不会找到它,因为合同是你通过它们的第一个字节来寻址块。

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

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

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

Rust将检查生命周期,因此切片不能超过缓冲区,并且进一步切片(s[..offset]s[offset..])不会分配。

如果您不介意一个分配,那么Vec::split_off会为分割部分分配一个足够大的新Vec