5

我有一个包含字节数组的结构。这个结构实际上来自于 bindgen 产生的 FFI 绑定,它的大小是在 C 代码中使用宏定义的,即:

C代码:

#define FOO_SIZE 100

struct the_struct
{
    char foo[FOO_SIZE];
    /* other fields... */
};

生成的 FFI 绑定:

pub struct the_struct {
    pub foo: [::std::os::raw::c_char; 100usize],
    // other fields...
}

我想确保来自 Rust API 端的数据适合foo. 我也不想FOO_SIZE在我的 Rust API 中硬编码,因为它可能会发生变化。

我知道这可以通过首先实例化 struct 来完成,但话又说回来,这需要显式初始化foo,如果不知道它的大小,这似乎是不可能的。此外,这是我想避免的额外步骤。

是否有可能在不实例化结构的情况下以某种方式获得foo静态的大小?如果没有,最好的方法是什么?更改 C 代码不是一种选择。

4

4 回答 4

2

我不知道是否有可能获得数组大小,但如果你没有太多这样的结构并且大小不会经常改变,我会明确声明这个值:

pub const FOO_SIZE: usize = 100;

然后声明一个如果硬编码常量错误将无法编译的函数:

fn _assert_foo_size(s: &mut the_struct) {
    s.foo = [0; FOO_SIZE];
}
于 2020-04-05T18:33:39.830 回答
2

在夜间频道上,我想出了这个:

#![feature(raw_ref_op)]

pub struct the_struct {
    pub foo: [::std::os::raw::c_char; 100usize],
    // other fields...
}

fn main() {
    let foo_size: usize = {
        fn size<T>(_: *const T) -> usize {
            std::mem::size_of::<T>()
        }

        let null: *const the_struct = std::ptr::null();
        size(unsafe { &raw const (*null).foo })
    };

    println!("{}", foo_size);
}

据我所知,&raw const (*null).foo不是 UB,因为明确允许取消引用空指针以获取另一个指针。不幸的是,这不仅需要仍然不稳定raw_ref_op的功能,而且因为 this 取消引用了一个指针,所以 this 也不能是const.

于 2020-04-05T19:46:33.210 回答
0

这适用于稳定的const环境,并且从 1.58 开始。

macro_rules! field_size {
    ($t:ident :: $field:ident) => {{
        let m = core::mem::MaybeUninit::<$t>::uninit();
        // According to https://doc.rust-lang.org/stable/std/ptr/macro.addr_of_mut.html#examples,
        // you can dereference an uninitialized MaybeUninit pointer in addr_of!
        // Raw pointer deref in const contexts is stabilized in 1.58:
        // https://github.com/rust-lang/rust/pull/89551
        let p = unsafe {
            core::ptr::addr_of!((*(&m as *const _ as *const $t)).$field)
        };

        const fn size_of_raw<T>(_: *const T) -> usize {
            core::mem::size_of::<T>()
        }
        size_of_raw(p)
    }};
}

pub struct Foo {
    pub foo: [u32; 34],
    // other fields...
}

// Stable as of 1.58:
const FOO_DATA_SIZE: usize = field_size!(Foo::foo);

fn main() {
    assert_eq!(field_size!(Foo::foo), 4 * 34);
}

addr_of!宏是稳定的,可以使用原始指针MaybeUninit<T>,但不能使用空指针。

于 2021-12-04T01:12:04.450 回答
0

您可以使用闭包来“模拟”评估,my_struct::foo而无需使用以下内容构建它:

pub struct the_struct {
    pub foo: [::std::os::raw::c_char; 100usize],
    // other fields...
}

pub fn main() {
    dbg!( get_size_of_return_type(|s: the_struct| s.foo) );
}

fn get_size_of_return_type<F, T, U>(_f: F) -> usize
where
    F: FnOnce(T) -> U
{
    std::mem::size_of::<U>()
}

操场

这只是让 rust 推断闭包的返回类型,U = [c_char; 100]给定 a fn(the_struct) -> U,并返回它的大小。

于 2021-12-04T10:00:05.960 回答