Search code examples
pointersrustunsafe

How do you create a ?Sized type from a raw pointer?


Here is an example of how to transmute a Sized type from a raw pointer:

use std::mem;

#[derive(Eq, PartialEq)]
#[repr(packed)]
struct Bob {
    id: u32,
    age: u32,
}

unsafe fn get_type<'a, T: Sized>(p: *const u8) -> &'a T {
    mem::transmute(p)
}

#[test]
fn it_works() {
    let bob = Bob {
        id: 22,
        age: 445,
    };
    let bob2: &Bob = unsafe {
        let ptr: *const u8 = mem::transmute(&bob);
        get_type(ptr)
    };
    assert_eq!(&bob, bob2);
}

However, for my application I want to be able to get a ?Sized type instead of a Sized type. However, this doesn't work:

unsafe fn get_type2<'a, T: ?Sized>(p: *const u8) -> &'a T {
    mem::transmute(p)
}

It fails with this error message:

error: transmute called with differently sized types: *const u8 (64 bits) to &'a T (pointer to T) [--explain E0512]
 --> src/main.rs:2:9
  |>
2 |>         mem::transmute(p)
  |>         ^^^^^^^^^^^^^^

I have tried to give it a &[u8] (fat pointer) by converting it using std::slice::from_raw_parts, but it fails with pretty much the same error message.


Solution

  • You actually cannot for the very reason cited in the error message.

    Rust references can be either pointer-sized (for Sized types) or bigger (for !Sized types). For example, if Trait is a trait, a &Trait reference is actually two fields as defined by std::raw::TraitObject.

    So, in order to form a reference to an unsized type, you have to:

    • identify exactly what kind of unsized type it is (trait? slice? ...)
    • pick the right representation (std::raw::TraitObject, std::raw::Slice, ...)

    and then you have to fill in the blanks (there is more than just a pointer).

    So, unless you can limit your function to producing &T where T: Sized, you cannot just transmute a raw pointer to &T.