How to get "raw" pointer from std::pin::Pin'ed object?

I have a Rust struct called MyCallback, and I need to pass a "raw" pointer to this struct to a native C function, where it will be remembered. This pointer will later be passed back to the application code, as a parameter of a callback function. Inside this callback function, I need to go from the "raw" pointer back to the original MyCallback struct and invoke a function on it.

Now, I understand that I need to use std::pin::Pin in order to pin my MyCallback object to a fixed memory address, so that the runtime won't move it around and the pointer remains valid for as long as the C library holds it. I have managed to put it into a Pin<Box<MyCallback>>, as in the examples, but how do I get from here a pointer that I can pass to the C library call?

Code looks like this:

pub struct MyContext {
    callback: Option<Pin<Box<MyCallback>>>
}

impl MyContext {
	pub fn set_callback(&mut self, my_callback: MyCallback) {
		self.callback.replace(Box::pin(my_callback));
		unsafe {
			native_api::set_callback(Some(callback_wrapper), self.callback.as_mut().unwrap() as *mut MyCallback as *mut c_void)
		}
	}
}

unsafe extern "C" fn callback_wrapper(user_data: *mut c_void) -> u32 {
    (&mut*(user_data as *mut MyCallback)).invoke()
}

But I get error:
casting &mut Pin<Box<MyCallback>> as *mut MyCallback is invalid

Does this all make sense? And if so, how do I resolve the error?

Also: Is it necessary that MyCallback struct also has the PhantomPinned marker?

Thank you!

Rust doesn't have a runtime (e.g. a GC) that moves stuff around randomly. Moves are explicit and always happen by-value.

Pin doesn't do anything. It's merely a type-level marker of pointer-like types with appropriate trait bounds (mainly involving Unpin), which makes sure that you can only get (mutable) references to the pointed object in safe code if the type itself guarantees a stable address.

You don't need the Pin here. Forget it. Just pass the address of the pointed object (e.g. by using &*the_box or Box::into_raw(the_box); I don't know whether or not you need an owned pointer.)

Since Box is a pointer-like type, it already guarantees that moving the box itself won't move its pointee.

4 Likes

Thanks for the info! Maybe I'm to much spoiled by how "pinning" is done in C# :sweat_smile:

So, does this look reasonable?

pub struct MyContext {
    callback: Option<Box<MyCallback>>
}

impl MyContext {
	pub fn set_callback(&mut self, my_callback: MyCallback) {
		self.callback.replace(Box::new(my_callback));
		let callback_ptr = self.callback.as_mut().unwrap().as_mut() as *mut MyCallback;
		unsafe {
			native_api::set_callback(Some(callback_wrapper), callback_ptr as *mut c_void)
		}
	}
}

The replace and the unwrap seems unnecessary. (Why not just assign, and why unwrap an optional when you already have the inner value?)

Especially because you are creating a mutable reference to self, the aliasing rules (&mut must always be exclusive) make it hard to use any aliasing pointers correctly.

I'd recommend working with raw pointers exclusively:

1 Like

I store the instance of MyCallback in the enclosing Context object, because I think as soon as MyCallback goes out of scope it will be dropped and the pointer will be "dangling", right? So, I have to store it somewhere where it will be kept "alive" for as long as the pointer is used...

Initially, Context doesn't have a callback instance, so it's an Option<Box<MyCallback>> with initial value of None. Will be replaced with the actual instance in set_callback().

Now, if I used Box::into_raw, this seems to consume the object. I assume this somehow makes Rust "forget" about the object but without dropping it, because otherwise the returned pointer would be "dangling" (invalidated) immediately. But then, how do I every drop the MyCallback instance, when it's due time? At some point we still want to perform a "clean" shut down.

(Also possible that set_callback() gets called again, with a different MyCallback instance)

In my code, I think, replace() drops the "old" callback object. Similarly, when the enclosing Context is dropped, eventually, the last active callback object is dropped along with it.

Read the code. I immediately convert the raw pointer back to a Box.

Assignment does the same. You can't cause leaks merely by assigning a new value to a place.

2 Likes

I see. Kinda missed that point.

Though I'm not sure whether dissolving the Box into a "raw" pointer via Box::into_raw(), just to re-create it immediately from the "raw" pointer, is preferable over a box.as_mut() as *mut MyCallback :thinking:

If my explanation of the UB resulting from aliasing &muts didn't convince you, I don't have anything else to offer.

Yes, raw *mut pointers derived from a &mut reference share the provenance with that reference. You are not allowed to create additional unrelated (i.e., non-reborrowed) &muts or &s to the same place (here, the context object pointed to by self) as long as such a pointer exists.

You are of course free to ignore the operational semantics of the language, but be advised that things may break in unexpected ways and at unexpected times if you do so.

1 Like