我可以使用 Box::from_raw 从 Vec::into_boxed_slice 释放内存吗?
Can I free memory from Vec::into_boxed_slice using Box::from_raw?
我saw下面的代码返回一个字节数组给C:
#[repr(C)]
struct Buffer {
data: *mut u8,
len: usize,
}
extern "C" fn generate_data() -> Buffer {
let mut buf = vec![0; 512].into_boxed_slice();
let data = buf.as_mut_ptr();
let len = buf.len();
std::mem::forget(buf);
Buffer { data, len }
}
extern "C" fn free_buf(buf: Buffer) {
let s = unsafe { std::slice::from_raw_parts_mut(buf.data, buf.len) };
let s = s.as_mut_ptr();
unsafe {
Box::from_raw(s);
}
}
我注意到 free_buf
函数采用 Buffer
,而不是 *mut u8
。这是故意的吗?
free_buf
函数是否可以简化为:
unsafe extern "C" fn free_buf(ptr: *mut u8) {
Box::from_raw(ptr);
}
您正确地注意到 C 运行时 free
函数仅将指向要释放的内存区域的指针作为参数。
但是,您不会直接调用它。事实上,Rust 有一个层抽象出实际使用的内存分配器:std::alloc::GlobalAlloc
.
提供这种抽象的原因是为了允许使用其他分配器,实际上是 quite easy 换出默认 OS 提供的分配器。
要求任何分配器跟踪块的长度以允许在不向释放函数提供长度的情况下释放它们是非常有限的,因此一般的释放函数也需要长度。
您可能有兴趣知道 C++ 有一个 similar abstraction. 提供了更多关于为什么要求应用程序跟踪已分配内存区域的长度而不是堆管理器的长度的讨论。
如果我们检查 Box::from_raw
的类型,我们会看到它从原始 *mut u8
构造了 Box<u8>
。人们需要一个 *mut [u8]
(指向切片的胖指针)来构造一个 Box<[u8]>
(这是我们一开始就有的)。
并且 drop
ping Box<u8>
将(最多)只释放一个字节的内存(如果不导致运行时错误),而 drop
ping Box<[u8]>
正确释放所有内存。
不,你所做的是未定义的行为,into_raw()
和from_raw()
之间的类型必须匹配。 Rust alloc API 不要求分配器记住任何信息,因此分配实现将期望传递给它的所有信息的正确性。
在您的示例中,*mut u8
和 *mut [u8]
是完全不同的类型,因此具有不同的布局。
此外,类型不匹配可能会阻止析构函数正确 运行。
您不能使用 from_raw()
来破坏任何指针,例如使用 void *
的 C free()
。
我saw下面的代码返回一个字节数组给C:
#[repr(C)]
struct Buffer {
data: *mut u8,
len: usize,
}
extern "C" fn generate_data() -> Buffer {
let mut buf = vec![0; 512].into_boxed_slice();
let data = buf.as_mut_ptr();
let len = buf.len();
std::mem::forget(buf);
Buffer { data, len }
}
extern "C" fn free_buf(buf: Buffer) {
let s = unsafe { std::slice::from_raw_parts_mut(buf.data, buf.len) };
let s = s.as_mut_ptr();
unsafe {
Box::from_raw(s);
}
}
我注意到 free_buf
函数采用 Buffer
,而不是 *mut u8
。这是故意的吗?
free_buf
函数是否可以简化为:
unsafe extern "C" fn free_buf(ptr: *mut u8) {
Box::from_raw(ptr);
}
您正确地注意到 C 运行时 free
函数仅将指向要释放的内存区域的指针作为参数。
但是,您不会直接调用它。事实上,Rust 有一个层抽象出实际使用的内存分配器:std::alloc::GlobalAlloc
.
提供这种抽象的原因是为了允许使用其他分配器,实际上是 quite easy 换出默认 OS 提供的分配器。
要求任何分配器跟踪块的长度以允许在不向释放函数提供长度的情况下释放它们是非常有限的,因此一般的释放函数也需要长度。
您可能有兴趣知道 C++ 有一个 similar abstraction.
如果我们检查 Box::from_raw
的类型,我们会看到它从原始 *mut u8
构造了 Box<u8>
。人们需要一个 *mut [u8]
(指向切片的胖指针)来构造一个 Box<[u8]>
(这是我们一开始就有的)。
并且 drop
ping Box<u8>
将(最多)只释放一个字节的内存(如果不导致运行时错误),而 drop
ping Box<[u8]>
正确释放所有内存。
不,你所做的是未定义的行为,into_raw()
和from_raw()
之间的类型必须匹配。 Rust alloc API 不要求分配器记住任何信息,因此分配实现将期望传递给它的所有信息的正确性。
在您的示例中,*mut u8
和 *mut [u8]
是完全不同的类型,因此具有不同的布局。
此外,类型不匹配可能会阻止析构函数正确 运行。
您不能使用 from_raw()
来破坏任何指针,例如使用 void *
的 C free()
。