如何在结束结束后留出空间以允许未来的ABI更改?

问题描述:

我想打电话给WSALookupServiceNext,这是一个extern C函数就像一个简化的接口:如何在结束结束后留出空间以允许未来的ABI更改?

你给它指向一个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; 
} 

这是可能的锈?如果不是,我应该如何分配空间大小>size_of::<Foo>(),但指向FooFoobindgen生成,所以我无法更改其声明并将字节添加到结尾。

我正在使用Rust 1.15.1。

+0

“*如果ABI改变了,问题*”......您是不是要说“API”?我是否正确地理解了你的想法:你想让你的Rust代码在C库中的不同版本的类型中工作?我认为你不应该那样做。你的Rust代码应该以结构的确切版本为目标,而不是像这样使用黑客来使其成为“一种工作”。如果您希望Rust代码与不同版本的C库一起使用,请使用货运功能和条件编译。 –

+0

@LukasKalbertodt很难决定API或ABI,但是由于C二进制更新后,您可以在不做任何源代码修改的情况下重建Rust程序,并继续工作,我称之为ABI。我无法定位准确版本,我希望我的代码能够尽可能多地使用Windows版本。因此,在我编写代码以使用Windows 10 SDK之后,我希望我的代码能够与Windows 11 SDK一起工作,无需进行修改和重建。可以在'C'上,为什么不在'Rust'上? – user1244932

+1

您也可以在Rust中添加一个哑元数组:'_filler_dummy:[u8; 4000],'。我只是说我认为这不是解决这个问题的好办法。但是再一次,我不太了解FFI的指导方针,所以也许别人可以澄清。 –

每日构建锈病已经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>()); 
} 

您可以使用一个枚举(也称为标记联合),它已经包含一些类型标记空间,并且访问内部值不太方便。 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], 
}