Does repr(transparent)
allow transmuting between two types if their parameters are repr(transparent)
? Basically is the following safe?
struct A<T>(T);
#[repr(transparent)]
struct B(*const ());
#[repr(transparent)]
struct C(*const ());
transmute::<A<B>, A<*const ()>>;
transmute::<A<C>, A<*const ()>>;
transmute::<A<B>, A<C>>;
The reason for the question is that I have a recursive data structure that is currently Arc
based. However the need for Arc
is only necessary for a few long lived values, for the shorter lived ones I would like to use arena allocation while still letting the arena allocated nodes refer to the Arc
allocated nodes without any copying. Since repr(transparent)
is gives the same layout and call convention I think this should be safe but I wasn't able to find out for sure that it applies for types with such types in type parameters.
use std::{fmt, marker::PhantomData, mem, ops::Deref, ptr::NonNull, sync::Arc};
// An "Arc" that points directly to the value instead of to the counters, followed by the value
#[repr(transparent)]
struct MyPtr<T>(NonNull<T>);
impl<T> MyPtr<T> {
fn new(value: T) -> Self {
unsafe {
MyPtr(NonNull::new_unchecked(
Arc::into_raw(Arc::new(value)) as *mut T
))
}
}
}
impl<T> Drop for MyPtr<T> {
fn drop(&mut self) {
unsafe {
Arc::from_raw(self.0.as_ptr());
}
}
}
impl<T> Deref for MyPtr<T> {
type Target = T;
fn deref(&self) -> &T {
unsafe { self.0.as_ref() }
}
}
impl<T> Clone for MyPtr<T> {
fn clone(&self) -> Self {
unsafe {
let a = Arc::from_raw(self.0.as_ptr());
mem::forget((a.clone(), a));
Self(self.0)
}
}
}
#[derive(Clone)]
#[repr(transparent)]
struct OwnedType(MyPtr<Type<OwnedType>>);
impl fmt::Debug for OwnedType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:#?}", *self.0)
}
}
impl OwnedType {
fn new(value: Type<OwnedType>) -> Self {
OwnedType(MyPtr::new(value))
}
}
#[derive(Clone)]
#[repr(transparent)]
struct RefType<'a>(NonNull<Type<RefType<'a>>>, PhantomData<&'a ()>);
impl<'a> Deref for RefType<'a> {
type Target = Type<RefType<'a>>;
fn deref(&self) -> &Self::Target {
unsafe { self.0.as_ref() }
}
}
impl<'a> fmt::Debug for RefType<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:#?}", **self)
}
}
#[derive(Debug)]
enum Type<T> {
Int,
Function(T, T),
}
impl Type<OwnedType> {
// Here is the transmute, the idea is that long lived `Arc` based values can be referred
// to as plain references. Thereby allowing cheaply created arena allocated references to
// transparently refer to long lived `Arc` references
fn as_ref<'a>(&'a self) -> &'a Type<RefType<'a>> {
unsafe { mem::transmute(self) }
}
}
fn main() {
let int = OwnedType::new(Type::Int);
let f = Type::Function(
OwnedType::new(Type::Function(int.clone(), int.clone())),
int.clone(),
);
println!("{:#?}", f);
println!("{:#?}", f.as_ref());
let int2: Type<RefType> = Type::Int;
let f2 = Type::Function(&int2, f.as_ref());
println!("{:#?}", f2);
}
Output:
Function(
Function(
Int,
Int
),
Int
)
Function(
Function(
Int,
Int
),
Int
)
Function(
Int,
Function(
Function(
Int,
Int
),
Int
)
)
Errors:
Compiling playground v0.0.1 (/playground)
Finished dev [unoptimized + debuginfo] target(s) in 0.57s
Running `target/debug/playground`