我需要Vec
在FFI中传递一些元素.通过实验,我发现了一些有趣的观点.我开始给FFI所有3 ptr
,len
和capacity
这样我就可以重建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()
似乎有一个保证,它将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); }
在操场上,它打印:
rustc 1.14.0 (e8a012324 2016-12-16) 0x7fdc9841b000. 0x7fdc98414018. 0x7fdc98414018
如果必须找到新的内存而不是在不造成副本的情况下脱掉额外的容量,这就不好了.
有没有其他方法可以将我的矢量传递到FFI(到C)而不传递容量?这似乎into_boxed_slice()
是我需要的,但为什么它涉及重新分配和复制数据?
原因相对简单.
现代内存分配器将在"大小"平板中分离分配,其中每个平板负责处理给定范围的大小.例如:
8字节slab:1到8个字节的任何内容
16字节slab:从9到16字节的任何内容
24字节slab:从17到24字节的任何内容
...
当你分配内存时,你要求一个给定的大小,分配器找到正确的slab,从中获取一个块,然后返回你的指针.
当你释放内存时...你怎么期望分配器找到合适的平板?有两种解决方案:
分配器有办法搜索包含你的内存范围的slab,不知何故,它涉及通过slab的线性搜索或某种全局查找表或...
你告诉分配器什么是分配块的大小
这里显而易见的是,C接口(free
,realloc
)相当低于标准,因此Rust希望使用更高效的接口,即onus在调用者上的接口.
所以,你有两个选择:
通过容量
确保长度和容量相等
如您所知,(2)可能需要新的分配,这是非常不受欢迎的.(1)可以通过全程传递容量来实现,也可以在某个时候存储容量,然后在需要时检索它.
而已.你必须评估你的权衡.