Unsafe rust: laying out structs dynamically

Consider the following snipplet:

use super::*;
use std::rc::Rc;


pub enum Object<T1, T3: Sized> {
    I32(i32),
    F32(f32),
    String(String),
    Rc(Rc<T1>),
    Struct(T3),
}

pub fn black_magic<T1, T3: Sized>(v: Vec<Object<T1, T3>>) {
   unimplemented!()
}

pub struct Foo {
    pub x: f32,
    pub y: f32,
    pub z: f32,
}

#[test]
fn test_00() {



    // returns similar memory layouta s (1 as i32, 2.0 as f32)
    black_magic(vec![
        Object::<(), ()>::I32(1 as i32),
        Object::<(), ()>::F32(2.0 as f32),
    ]);


    // returns similar memory layouta s
    // (1 as i32, Foo{x: 1.0, y: 2.0, z: 3.0}, 2.0 as f32)
    black_magic(vec![
        Object::<(), Foo>::I32(1 as i32),
        Object::<(), Foo>::Struct(Foo{x: 1.0, y: 2.0, z: 3.0}),
        Object::<(), Foo>::F32(2.0 as f32),
    ]);

}

How do I define the black_magic function so that it (1) allocates space of appropriate size, returns something that is not (), properly aligns fields, and copies the data over?

Yes, I know this is terrible and should not be done in practice. I’m trying to understand this purely for educational purposes with regard to unsafe rust.

I’m not sure what you’re trying to do. Can you elaborate a bit more on your goal?

allocates space of appropriate size

Allocates space for what? Vec<Object<T1, T3>> already stores its content on the heap.

returns something that is not ()

What should it return, then? What should the function do, exactly?

Since you seem to be interested in manual allocation, you can take a look at std::mem::size_of and std::mem::align_of functions that will tell you the size and alignment requirements of a given type. You can also use Layout::new to create a Layout representing the memory requirements of your type and then pass it to std::alloc::alloc to make the allocation.

Note that T1, T3: Sized means Sized is only applied to T3 and not T1. But also note that Sized bound is applied by default, so T1 and T3 will still be both Sized even if you don’t explicitly specify the Sized bound. You can opt-out of Sized bound with T: ?Sized.

did you leave a -> ReturnType out of your black_magic signature?

I agree there are parts of the question that are not clear. I will rethink how to best ask this question (perhaps writting C / broken Rust that better illustrates the unsafe ops I want done.)

I think I may have gotten the general picture from the comments?

// For this input
black_magic(vec![
    Object::<(), ()>::I32(1 as i32),
    Object::<(), ()>::F32(2.0 as f32),
]);

// you would like it to return something like this?
Box::new((1 as i32, 2.0 as f32)) as Box<Any>

Just to maybe clear one thing up: Do you need the value to be laid out as a tuple? That’s gonna be really hard; you can’t build the value incrementally since the memory layout is unspecified (and rust freely reorders fields to fill padding and etc.)

Something that miiiiiiight be easier is to lay it out like a cons list (like frunk’s HCons but with a ?Sized tail…maybe?) …but probably not.

The thing is, the advantage of HList over tuples is that you can build them incrementally, but I still don’t think you can incrementally construct a ?Sized list with the same layout as Hlist![A, B, C, D]… the fundamental problem being that, if you are building it incrementally, then I think you are forced to add one level of indirection for each value you insert. (otherwise, rust would need to codegen an infinite number of monomorphizations somewhere…).

…yeah…nope, this is just plain ugly.

:man_shrugging::white_flag: