在没有重新分配的情况下将Vec转换为FFI的正确方法是什么?

时间:2017-01-18 15:56:39

标签: vector rust ffi

我需要在FFI中传递Vec个元素。通过实验,我发现了一些有趣的观点。我开始给予FFI全部3:ptrlencapacity,以便我可以重建Vec以便稍后销毁它:

let ptr = vec.as_mut_ptr();
let len = vec.len();
let cap = vec.capacity();
mem::forget(vec);
extern_fn(ptr, len, cap);

// ...

pub unsafe extern "C" fn free(ptr: *mut u8, len: usize, cap: usize) {
    let _ = Vec::from_raw_parts(ptr, len, cap);
}

我想摆脱capacity因为它对我的前端没用;它只是为了让我可以重建我的矢量以释放记忆。

Vec::shrink_to_fit()很有吸引力,因为它似乎消除了处理能力的需要。不幸的是,它上面的文档并不能保证它会生成len == capacity,因此我假设在from_raw_parts()期间可能会触发未定义的行为。

into_boxed_slice()似乎可以保证它会从docs生成len == capacity,所以我接下来会使用它。 如果我错了,请纠正我 。问题是它似乎不能保证不重新分配。这是一个简单的程序:

fn main() {
    let mut v = Vec::with_capacity(1000);
    v.push(100u8);
    v.push(110);
    let ptr_1 = v.as_mut_ptr();
    let mut boxed_slice = v.into_boxed_slice();
    let ptr_2 = boxed_slice.as_mut_ptr();
    let ptr_3 = Box::into_raw(boxed_slice);
    println!("{:?}. {:?}. {:?}", ptr_1, ptr_2, ptr_3);
}

In the playground,打印:

rustc 1.14.0 (e8a012324 2016-12-16)
0x7fdc9841b000. 0x7fdc98414018. 0x7fdc98414018

如果必须找到新的内存而不是在不造成副本的情况下脱掉额外的容量,那就不好了。

有没有其他方法可以将我的矢量传递到FFI(到C)而不传递容量?似乎into_boxed_slice()是我需要的,但为什么它涉及重新分配和复制数据?

1 个答案:

答案 0 :(得分:8)

原因相对简单。

现代内存分配器将在#34; size"中分配分配。平板,每个平板负责处理给定范围的尺寸。例如:

  • 8字节slab:1到8个字节的任何内容
  • 16字节slab:从9到16字节的任何内容
  • 24字节slab:从17到24字节的任何内容
  • ...

当你分配内存时,你要求一个给定的大小,分配器找到正确的slab,从中获取一个块,然后返回你的指针。

当你释放内存时...你怎么期望分配器找到合适的平板?有两种解决方案:

  • 分配器有办法搜索包含你的内存范围的slab,不知何故,这涉及到通过slab的线性搜索或某种全局查找表或...
  • 告诉分配器分配块的大小是什么

这里显而易见的是,C接口(freerealloc)相当低于标准,因此Rust希望使用更有效的接口,而不是使用更有效的接口在打电话上。

所以,你有两个选择:

  1. 传递容量
  2. 确保长度和容量相等
  3. 如您所知,(2)可能需要分配,这是非常不受欢迎的。 (1)可以通过全程传递容量来实现,也可以在某个时候存储容量,然后在需要时检索它。

    那就是它。你必须评估你的权衡。