How to correctly convert an Indirect Raw Pointer into a Struct?

I'm currently returning an opaque *const () from my Arc::raw function and turn it back into an Arc via Arc::from_raw. However, I also need to be able to dereference the raw pointer to access the data immutably without constructing an Arc -- it has a custom drop implementation.

For the moment, I'm providing the function Arc::data_from_raw, but I'd love to get rid of it. However, I'm not entirely sure how I can return a *const T from Arc::raw and have it be used as an argument to Arc::from_raw without accidentally messing up and introduce undefined behavior into my code.

Here's the relevant code:

#[repr(align(128))]
struct Align128<T>(T);

#[repr(align(128))]
struct RawArc<T> {
	count: Align128<std::AtomicIsize>,
	data: T,
}

pub struct Arc<T> {
	ptr: std::NonNull<RawArc<T>>,
	phantom: std::PhantomData<RawArc<T>>,
}

impl<T> Arc<T> {
	pub fn raw(data: T) -> *const () {
		std::Box::into_raw(std::Box::new(RawArc {
			count: Align128(std::AtomicIsize::new(0)),
			data,
		})) as _
	}

	pub unsafe fn from_raw(ptr: *const ()) -> Self {
		Self {
			ptr: std::NonNull::new_unchecked(ptr as _),
			phantom: std::PhantomData,
		}
	}
}

impl<T: std::Copy> Arc<T> {
	pub unsafe fn data_from_raw(ptr: *const ()) -> T {
		(*(ptr as *const RawArc<T>)).data
	}
}
1 Like

A bit of googling found me the field_offset crate which could, perhaps, help you convert between *const RawArc<T> and the corresponding *const T pointing to the data field.

1 Like

But remember to add repr(C) everywhere, as the layout of Rust structures without a repr(C) clause can vary from structure to structure within the same compilation. Rust reserves to itself the right to make any and all optimizations of layout, including from profile-guided-optimization, unless you explicitly add a repr(…) constraint.

2 Likes

I took a deep look into the crate you suggested and more importantly, its dependency memoffset. As it turns out, the API heavily depends on UB behaving defined and at its core lies the same issue as taking raw pointers to fields of packed structs. Once raw_ref_macros has been stabilized and has been shipped with a Rust release, I'm most likely going to use memoffset directly. The main reason is, that it does not require changing the layout representation away from repr(Rust).

Here's the issue of interest:

For now, the only viable solutions seem to either use repr(C), as suggested by @TomP and manually hard-code the offset as a constant or keep doing what I've been doing, so far. I'm not in a hurry to release the crate, tho, so with a bit of luck, I may not have to wait too long to use memoffset.

Thank you both! :heart: