Having a Wrapper(T,)
, from a &Wrapper
you can get a &T
, thanks to being able to project a reference to a struct into a reference to one of its fields (e.g., &wrapper.0
).
However, in general, you cannot go the other way around: that is, going from a &T
to a &Wrapper
, since nothing guarantees the existence of the "wrapping bread" around the pointee T
.
An example of this is that your PathSlice
could be / become (e.g., after a refactoring):
type PathSegments = [PathSegment];
struct PathSlice (bool, PathSegments);
// or similarly
enum PathSlice {
A(PathSegments),
B(PathSegments),
}
Here we can clearly see why it is not possible to get a &PathSlice
from a &PathSegments
(and with the type alias it should be easier to spot that unsizedness is not the problem here; you would have had a "cannot return a reference to a local / the local does not live long enough" error in your example): nothing guarantees that a valid bool
precedes the PathSegments
(and that is without talking into account alignment & padding).
The general solution is to wrap the reference, like you tried:
#![deny(elided_lifetimes_in_paths)]
struct PathSlice<'pathbuf> (
&'pathbuf PathSegments,
);
impl PathBuf {
fn as_slice (self: &'_ PathBuf) -> PathSlice<'_>
{
PathSlice(&self.0)
}
}
But this is not compatible with AsRef::as_ref
signature (and would require a second wrapper for the &mut
case).
The solution, for you case, is to assert (making the compilation fail when it is not the case, hence guarding against such refactoring) that your wrapper only exists at the type level; that on runtime both the wrapper and the wrappee are structurally equivalent; that your wrapper is a transparent wrapper. Hence the need for the #[repr(transparent)]
attribute:
#[repr(transparent)]
struct Wrapper /* = */ (
Wrappee,
);
With that attribute, it becomes sound (although it still requires unsafe
; I guess that a proc_macro_attribute
granting a non-unsafe
transmuting API would be a nice thing to do) to transmute between Wrapper
and Wrappee
, or between any pointer to Wrapper
and the same pointer to Wrappee
:
-
Wrapper = Wrappee
,
-
&'a Wrapper = &'a Wrappee
(and *const _
),
-
&'a mut Wrapper = &'a mut Wrappee
(and *mut _
),
-
Box<Wrapper> = Box<Wrappee>
(and NonNull<_>
),
-
there should be no reason that it does not work also with Arc<Wrapper> = Arc<Wrappee>
, but there is currently no warranty w.r.t. the transitivity of structural equivalence when one of the structs is neither #[repr(transparent)]
nor #[repr(C)]
, and in the case of Arc
/ Rc
a ArcBox
/ RcBox
#[repr(Rust)] struct
wraps the pointee.
Finally, unsizedness makes this more subtle, given that there is an implicit len: usize
(or vtable: *const ()
) field carried around during the transmutation of pointers.
Hence @vitalyd's suggested solution.