This is something of a follow-up to a previous question of mine. TL;DR: I tried to create a self-referencing struct by heap allocating the self-referencee using a Box
. It was pointed out that I can't rely on the pointer provenance not changing (it is currently undecided).
This led to me trying to implement a sort of custom Box
, PinnedClient
, which allocates to the heap when created and deallocates on 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>());
}
}
}
Then I include a PinnedClient
in my struct and use the raw pointer to create the self-reference:
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 }
)
}
}
Now I am wondering:
- is it undefined behaviour to "hand out" a mutable reference to the
Client
and - is it it actually guaranteed that the memory gets deallocated (does
dealloc
get called in all scenarios)?
I am somewhat anxious since self-referential structs are supposed to be hard and I feel like I am missing something.