为什么我的自引用结构不起作用(无效的内存引用)?

问题描述 投票:0回答:1

我正在尝试在 Rust 中实现一个自引用结构。

背景

我正在写一个基于另一个板条箱的板条箱。底层板条箱公开了一个类型

Transaction<'a>
,它需要对
Client
对象的可变引用:

struct Client;

struct Transaction<'a> {
    client: &'a mut Client
}

构造

Transaction
的唯一方法是调用客户端对象上的方法。该函数看起来像这样:

impl Client {
    async fn transaction<'a>(&'a mut self) -> Transaction<'a> {
        // ...
    }
}

现在我想公开一个允许这样的API:

let transaction = Transaction::new().await;

当然,如果不通过

&mut Client
,这是行不通的。

现在我的想法是创建一个包装类型,它拥有一个

Client
以及一个
Transaction
并引用所述客户端。

我将使用

unsafe
创建一个可变引用,该引用可用于创建
Transaction
,同时仍然能够将
Client
对象传递给我的结构。我会将
Client
固定在堆上,以确保引用不会变得无效:

struct MyTransaction<'a> {
    client: Pin<Box<Client>>,
    inner: Transaction<'a>
}

impl<'a> MyTransaction<'a> {
    async fn new() -> MyTransaction<'a> {
        // ... fetch a new client object
        let pin = Box::pin(client);
        let pointer = &*pin as *const Client as *mut Client;
        
        // convert raw pointer to mutable reference 
        // to create new Transaction
        let inner = unsafe {
            &mut *pointer
        }.transaction().await;

        Self {
            inner,
            client
        }
    }
}

但是,当我运行此代码时,我的测试失败并显示以下错误消息:

error: test failed

Caused by:
  process didn't exit successfully: `/path/to/test` (signal: 11, SIGSEGV: invalid memory reference)

这让我有些惊讶,因为我认为通过固定

client
对象我可以确保它不会被移动。因此,我推断,只要它的寿命不超过客户端,对它的指针/引用就不应该变得无效。我认为情况不可能是这样,因为只有当
MyTransaction
被删除时客户端才会被删除,这也会删除保存引用的
Transaction
。我也认为移动
MyTransaction
应该是可能的。

我做错了什么?预先感谢!

asynchronous rust self-reference rust-pin
1个回答
0
投票

这里的问题是字段上的隐式删除顺序,在 Rust 中,该顺序是声明顺序,这意味着您的

client
inner
之前被删除,因此当
inner
被删除并使用它对
client
的引用时,该引用悬空并导致您看到 SIGSEGV。 一个简单的解决方法就是重新排序它们:

struct MyTransaction<'a> {
    inner: Transaction<'a>
    client: Pin<Box<Client>>,
}

对于更复杂的场景,请参阅强制删除结构体字段的顺序

© www.soinside.com 2019 - 2024. All rights reserved.