repr(C)和repr(rust)有什么区别?

12

我正在编写一个Vulkan渲染器,我意识到我应该只接受repr(C)类型,但据我所知,没有办法在编译时实际检查。

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

经过几次尝试,我终于能够看到repr(c)repr(rust)之间的区别了,但只有在使用Box时才能看到。

repr(C)repr(rust)之间有什么区别?我可以假设如果一个类型只包含其他POD类型,那么布局将与C中的布局相同吗?

例如:

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);

源代码

我正在填充一个缓冲区,然后将其交给Vulkan使用,因此我认为这里的布局可能很重要。


“我应该只接受 repr(C) 类型。” -> 你能解释一下吗?你在某些方法中接受通用类型并需要它们是 repr(C) 类型吗?为什么? - Lukas Kalbertodt
我认为Rust不保证使用与C相同的数据表示形式。 - user4815162342
@LukasKalbertodt 我不完全确定你的意思,但我在我的问题中添加了一个示例。当然,这里的 Vertex 应该与着色器将要使用的 C 布局相匹配吧?我不太确定 #[repr(C)] 是否也会因为不同的打包而导致不同的大小。 - Maik Klein
1个回答

9
你程序输出的差异不是由于内存布局所致。`Box`堆分配并存储指向堆内容的指针,因此你打印的是指针。由于`Box`不执行任何内部化/对象池操作,两个地址当然是不同的。可能有点令人困惑的是这些地址非常接近。我猜这只是与Rust使用的分配器jmalloc有关,它为小型分配提供了密集的池。
“如果一个类型只包含其他POD类型,我可以假设布局与C中相同吗?” 不行。 你几乎不能假设任何关于Rust类型的内存布局的事情。这是故意没有指定的,以便进行优化,例如字段重新排序。即使现在,`repr(Rust)`与`repr(C)`非常相似,你也不能保证它将永远如此。

噢,没错,我简直不敢相信我竟然没注意到那个。如果类型具有#[repr(C)],那么我想我可以使用 proc 宏来动态地实现某些 trait。 - Maik Klein
2
特别是,最近有一些领域被重新排序了,但这一举措已经暂时撤回:https://internals.rust-lang.org/t/rolling-out-or-unrolling-struct-field-reorderings/4485。 - Chris Emerson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接