这是我的上一个问题的后续问题。 TL;DR:我尝试通过使用
Box
堆分配自引用来创建自引用结构。有人指出,我不能依赖分配对象的地址不变(目前尚未决定)。
这导致我尝试实现一种自定义
Box
,PinnedClient
,它在创建时分配给堆并在Drop
上释放:
struct PinnedClient(pub *mut Client);
impl PinnedClient {
unsafe fn new(client: Client) -> PinnedClient {
// Allocate memory on the heap
let layout = Layout::new::<Client>();
let pointer = alloc(layout) as *mut Client;
// Make sure it worked
if pointer.is_null() {
handle_alloc_error(layout);
}
// Move the client object to the heap
pointer.write(client);
// Return a `PinnedClient` object with a pointer
// to the underlying client.
PinnedClient(pointer)
}
}
impl Drop for PinnedClient {
fn drop(&mut self) {
// Deallocate the previously allocated when
// wrapper is dropped.
unsafe {
dealloc(self.0 as *mut u8, Layout::new::<Client>());
}
}
}
然后我在结构中包含一个
PinnedClient
并使用原始指针创建自引用:
pub struct MyTransaction<'a> {
transaction: Transaction<'a>,
_client: PinnedClient
}
impl<'a> MyTransaction<'a> {
async fn from_client<'this>(client: Client) -> Result<MyTransaction<'a>, Error> {
let client = unsafe { PinnedClient::new(client) };
let transaction = unsafe {
// Convert `*mut Client` to `&mut Client`
// This shouldn't fail since the pointer in PinnedCliend
// is guaranteed not to be null.
&mut *client.0
}.transaction().await?;
Ok(
MyTransaction { _client: client, transaction }
)
}
}
现在我想知道:
Client
和 dealloc
吗)?我有点焦虑,因为自我参照结构应该很难,我觉得我错过了一些东西。
“分发”对
的可变引用是否是未定义的行为Client
不,不是(当然,假设您保留所有别名规则并且不分发two这样的引用)。
但是,正如您之前的问题中所指出的,您需要交换字段,因为目前
Transaction
在 Client
之后被删除,但它可能在删除期间访问它,从而导致释放后使用。
它实际上保证了内存被释放(在所有情况下都会调用
吗)?dealloc
是和不是。
不,因为 在 Rust 中,不保证会调用析构函数(除非
Pin
有特殊要求)。如果 MyTransaction
的析构函数未运行(由于 std::mem::forget()
或由于 Rc
循环或其他原因),则不会释放内存。
但也是肯定的,因为问题你可能想问的答案是肯定的:如果指针是
Box
,则每次内存被释放时,也将与你的智能指针一起释放。 Box
也使用Drop
,它并不特殊(但在其他方面很特殊)。
但是...
你又错过了。您的代码仍然泄漏资源。
Client
的后备内存
将被释放,但
Client
本身不会被删除。这意味着,如果您的客户端拥有一些资源(堆内存、文件描述符、网络连接等),它们将不会被释放。 即使
当前Client
没有任何滴胶,如果它来自您无法控制的库,则没有任何东西可以保证它将来不会继续没有滴胶。添加
Drop
impl 不被视为重大更改。所以你仍然需要放弃它,以适应未来。幸运的是,这很容易:
impl Drop for PinnedClient {
fn drop(&mut self) {
// Deallocate the previously allocated when
// wrapper is dropped.
unsafe {
self.0.drop_in_place();
dealloc(self.0 as *mut u8, Layout::new::<Client>());
}
}
}
因为自引用结构应该很困难你在这里问了两个问题,你两次微妙地失败了,无法编写无 UB 代码。这对你来说还不够难吗?