如何强制Rust接管由其安全方法之外分配的内存?

8
在他2018年2月的名为“Rust中的内存安全:C语言案例研究”的笔记中,Will Crichton写道:
Rust提供了获取原始指针所有权的能力,我们使用slice::from_raw_parts_mutBox::from_raw来告诉Rust将内存指针视为堆分配的数组。在转移所有权后,假设内存有效且大小/类型正确,Rust会应用其通常的内存安全性和包容性检查。
上述内容所指的他代码的相关部分是:
let mut new_data = unsafe {
    let ptr = Heap::default()
        .alloc(Layout::array::<isize>(new_capacity).unwrap())
        .unwrap() as *mut isize;
    Box::from_raw(slice::from_raw_parts_mut(ptr, new_capacity))
};

然而,Box::from_raw 的文档说明(重点强调):

由于 Box 分配和释放内存的方式是未指定的,此函数的唯一有效指针 是通过 Box::into_raw 函数从另一个 Box 中获取的指针。

为了避免疑问,上述使用(已在 Rust 1.27.0 中删除的)实验性 Heap API 执行内存分配,直接在其 alloc 方法中调用了 __rust_alloc,因此 ptr 并非通过 Box::into_raw 获得。

即使不被支持,将指向新分配的内存的原始指针传递给Box::from_raw以使Rust接管该内存并执行其通常的安全性和包含性检查是否有效?特别是当产生的Box被销毁时,Rust会释放那块内存吗?

如果不能,那么如何强制Rust接管通过其安全方法之外分配的内存所有权?


2
2019年2月5日相关PR - Lukas Kalbertodt
根据定义,这是没有意义的。如果不这样做,那么怎样可以强制 Rust 接管通过其安全方法分配的内存之外的内存?此外,我建议不要使用 Box::from_raw 来处理非 Box 所提供的指针,即使在某些情况下这可能是正确的,它也可能很快出错。另外,我怀疑一个良好的 C API 是否会关心资源释放,除了一些情况,你可以调用 free。我认为最好调用 free 而不是猜测是否需要使用 box。 - Stargateur
1个回答

7

即使是未得到支持的,把指向新分配内存的裸指针传递给 Box::from_raw,这样做是有效的吗?

不,这是无效的。

特别地,当产生的 Box 被销毁时,Rust 是否会释放该内存?

是的,这就是为什么它是无效的原因。

内存分配器提供了成对的分配和释放例程。当你使用一个分配器分配一块内存时,你必须使用该分配器释放它

如果你没有这样做,当执行释放操作的分配器进行所需的记账时,它将不知道那块内存。实际进行分配的分配器永远不会将该内存标记为不可用。

这些问题并非杞人忧天。我曾经提交了 GLib 补丁,以纠正发生在野外的不匹配的分配/释放导致的真正问题。

Rust 接管分配的内存

在原始指针的层面上,所有权主要是一种心态,就像在C或C++中一样。在这里拥有某个东西意味着你需要负责适当地清理它。
malloc和free是成对的分配/释放方法。您可以创建自己的类型并为其实现Drop:
use libc::{free, malloc};
use std::{ffi::c_void, mem};

struct MallocBox(*mut i32);

impl MallocBox {
    fn new(v: i32) -> Self {
        unsafe {
            let p = malloc(mem::size_of::<i32>()) as *mut i32;
            *p = v;
            Self(p)
        }
    }
}

impl Drop for MallocBox {
    fn drop(&mut self) {
        unsafe { free(self.0 as *mut c_void) }
    }
}

fn main() {
    MallocBox::new(42);
}

一个真正的实现还应该实现 Deref 和许多其他特质,以使这种类型易于使用。

如果不提供分配器共享特质,就需要创建 MallocBoxJeMallocBoxMyCustomAllocBox,这将非常麻烦,因此 RFC 1398 提出了为分配器提供共享特质的建议。相关的工作正在进行,将 Box<T> 转换为 Box<T, A: Alloc + Default = Global>

如何强制 Rust?

在低级细节方面,Rust 没有 "强制" 做任何事情,更不用说这种情况了。例如,不能保证分配指针的 C 代码不会尝试自己释放指针。在 FFI 的世界中,所有权是一项合作协议。


另请参阅:


2
我实际上正在寻找一种方法从Rust内部分配内存(以实例化自定义DST)。我的问题是,如果我使用Rust的分配器来分配内存,然后用(正确类型的)fat raw指针指向该内存,那么这样的指针是否可以传递给Box :: from_raw,以便Rust承担所有进一步的内存责任(即所有权,借用,释放等)? - eggyal

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接