Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the difference between repr(C) and repr(rust)?

Tags:

rust

I am currently writing a Vulkan renderer and I just realized that I should only accept types that are repr(C), but as far as I know there is no way to actually check that at compile time.

struct Vertex {
    x: f32,
    y: f32,
    b: Box<f32>
}

#[repr(C)]
struct Vertex2 {
    x: f32,
    y: f32,
    b: Box<f32>
}

fn to_bytes<T>(t: &T) -> &[u8]{
    let p: *const T = t;
    let p = p as *const u8;
    unsafe{
        std::slice::from_raw_parts(p, std::mem::size_of::<T>())
    }
}

fn main() {
    let v = Vertex{x: 42.0, y: 0.0, b: Box::new(42.0)};
    let v2 = Vertex2{x: 42.0, y: 0.0, b: Box::new(42.0)};
    println!("{:?}", to_bytes(&v));
    println!("{:?}", to_bytes(&v2));
}

Playground

After a few tries, I could finally see a difference between repr(c) and repr(rust) but only when I used a Box.

What is the difference between repr(C) and repr(rust)? Can I assume that if a type only contains other POD types, then the layout will be the same as in C?

Example:

let slice = base.device
    .map_memory::<Vertex>(vertex_input_buffer_memory,
                          0,
                          vertex_input_buffer_info.size,
                          vk::MemoryMapFlags::empty())
    .unwrap();
slice.copy_from_slice(&vertices);

Source

I am filling a buffer that I hand to Vulkan, so I assume that the layout here probably matters.

like image 722
Maik Klein Avatar asked Mar 16 '17 09:03

Maik Klein


People also ask

What does repr c do Rust?

The interaction of repr(C) with Rust's more exotic data layout features must be kept in mind. Due to its dual purpose as "for FFI" and "for layout control", repr(C) can be applied to types that will be nonsensical or problematic if passed through the FFI boundary.

What is alignment in Rust?

The alignment of a value specifies what addresses are valid to store the value at. A value of alignment n must only be stored at an address that is a multiple of n. For example, a value with an alignment of 2 must be stored at an even address, while a value with an alignment of 1 can be stored at any address.


1 Answers

The difference you see in the output of your program is not due to memory layout. Box<T> heap allocates and stores a pointer to the heap contents, so what you are printing is the pointer. As Box<T> doesn't perform any interning/object pooling, the two addresses are of course different. What's probably a bit confusing is that the addresses are so close to each other. I guess this has just something to do with jmalloc, the allocator Rust uses, which has densely packed pools for small allocations.

Can I assume that if a type only contains other POD types, then the layout will be the same as in C?

No. You pretty much can't assume anything about Rust's memory layout of types. This is intentionally not specified to allow for optimizations, such as field reordering. Even though right now, repr(Rust) matches repr(C) pretty closely, you can't assume that it will be like that forever.

like image 130
Lukas Kalbertodt Avatar answered Sep 28 '22 06:09

Lukas Kalbertodt