如何在结构结束后留出空格以允许将来的ABI更改?

时间:2017-02-26 12:45:55

标签: rust

我想调用WSALookupServiceNext,这是一个extern C函数,具有简化的界面,如:

#[repr(C)]
pub struct Foo {
    version: u32,
    a: u32,
}

extern "C" {
    pub fn get_foo(p: *mut Foo, nbytes: *mut u32) -> i32;
}

你给它指向Foo结构和nbytes = ::std::mem::size_of::<Foo>()的指针,如果一切正常,它会填充结构。

如果在编译程序后ABI发生变化,请执行以下操作:

#[repr(C)]
pub struct Foo {
    version: u32,
    a: u32,
    b: f64,
}

然后该函数指示失败,nbytes将包含Foo的正确大小。我该如何处理这种情况?

在C或C ++中,我会简化我的生活并做:

union {
  struct Foo foo;
  char buf[4000];//should be enough for 10 years
} a;
uint32_t nbytes = sizeof(a);
if (get_foo(&a.foo, &nbytes) != 0) {
  return -1;
}

这可能在Rust吗?如果没有,我应该如何分配大小&gt; size_of::<Foo>(),但指针指向FooFoobindgen生成,因此我无法更改其声明并将字节添加到其末尾。

我正在使用Rust 1.15.1。

2 个答案:

答案 0 :(得分:1)

您可以使用枚举(也称为标记联合),它已经为类型标记包含一些空间,并且访问内部值不太方便。 Nightly builds of Rust have untagged unions,但这还不稳定,阅读或借用它的字段需要不安全的代码。由于评论表明您可以手动分配。直接的方法是使用包含结构的结构,分配外部:

#[repr(C)]
pub struct Foo {
    version: u32,
    a: u32,
}

#[repr(C)]
pub struct FooPadded {
    foo: Foo,
    _reserved: [u8; 4000],
}

答案 1 :(得分:1)

每晚构建的Rust都有untagged unions,因此您可以使用与C / C ++相同的解决方法。

#![feature(untagged_unions)]

#[repr(C)]
pub struct Foo {
    version: u32,
    a: u32,
    b: f64,
}

#[repr(C)]
union PaddedFoo {
    foo: Foo,
    _padding: [u8; 4000],
}

fn main() {
    println!("{}", std::mem::size_of::<Foo>());
    println!("{}", std::mem::size_of::<PaddedFoo>());
}