Whether to impl AsRef, Borrow, Deref, or something else for (owning|ref-containing) struct pair

Example structs:

struct ExampleStruct {
    field_1: u32,
    field_2: Box<[u8]>
}
struct ExampleStructRef<'a> {
    field_1: u32,
    field_2: &'a [u8]
}

I'd like to write a function that takes an &ExampleStruct and produces an &ExampleStructRef (similarly to how one can go from &String to &str), but I'm not sure how this can be done, or what trait I should impl (AsRef, Borrow, Deref, or something else) to contain this conversion. (I suspect that this may require fixing the struct layouts via #[repr(C)] and doing transmutes that count on Box<T> and &T having the same representation as a pointer, but I'm hoping to avoid that unless there's no other way to do this.)

Your entire idea of converting a reference with one pointed type to a reference with a different pointed type fundamentally requires that the layout of the two types be the same. The operation that you are describing is itself a transmute, in essence.

Since the layout of fat pointers is not guaranteed, this cannot be done reliably (ie., without UB).

You could use a single struct layout for both purposes:

struct Inner {
    field_1: u32,
    field_2: *const [u8],
}
#[repr(transparent)]
pub struct ExampleStruct {
    /// The `field_2` in this must be a `Box::into_raw()` pointer
    data: Inner,
}
#[repr(transparent)]
pub struct ExampleStructRef<'a> {
    data: Inner,
    _phantom: PhantomData<&'a [u8]>,
}

Then you have to use unsafe in your accessor functions and Drop implementation for ExampleStruct, but it's sound to pointer-cast an &'a ExampleStruct into an &'b ExampleStructRef<'a> where 'a: 'b, as long as ExampleStructRef doesn't expose any operations that would drop or mutate the field_2 data.

Well, if we assume we are allowed to change the premise of the question, then a much better alternative is to not use any unsafe at all, lose the struct containing borrowed data, and just return a reference to the owned one. Then there could be a conversion from &'a Owned to Borrowed<'a>:

struct Owned {
    field_1: u32,
    field_2: Box<[u8]>
}

struct Borrowed<'a> {
    field_1: u32,
    field_2: &'a [u8]
}

impl<'a> From<&'a Owned> for Borrowed<'a> {
    fn from(owned: &'a Owned) -> Self {
        Borrowed {
            field_1: owned.field_1,
            field_2: &*owned.field_2,
        }
    }
}

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.