简体   繁体   English

如何在不实例化的情况下获取 Rust 中结构字段的大小

[英]How to get the size of a struct field in Rust without instantiating it

I have a structure with a byte array in it.我有一个包含字节数组的结构。 This structure actually comes from the FFI bindings produced by bindgen, and its size is defined in C code using a macro, ie:这个结构实际上来自于bindgen产生的FFI绑定,它的大小是在C代码中使用宏定义的,即:

C code: C 代码:

#define FOO_SIZE 100

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

Generated FFI bindings:生成的 FFI 绑定:

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

I want to make sure that the data coming from the Rust API side fits into foo .我想确保来自 Rust API 端的数据适合foo I also do not want to hard code FOO_SIZE in my Rust API, since it is subject to change.我也不想在我的 Rust API 中硬编码FOO_SIZE ,因为它可能会发生变化。

I understand that this can be done by instantiating the struct first, but then again, that would require explicit initialization of foo , which seems to be impossible without knowing its size.我知道这可以通过首先实例化结构来完成,但话又说回来,这需要显式初始化foo ,这在不知道它的大小的情况下似乎是不可能的。 Furthermore, it is an extra step I want to avoid.此外,这是我想避免的额外步骤。

Is it possible to somehow get the size of foo statically without instantiating the structure?是否可以在不实例化结构的情况下以某种方式静态获取foo的大小? If not, what would be the best approach?如果没有,最好的方法是什么? Changing C code is not an option.更改 C 代码不是一种选择。

I do not know if it is possible to get the array size yet, but if you don't have too many such structures and the size doesn't change too often, I'd just declare the value explicitly:我不知道是否有可能获得数组大小,但如果你没有太多这样的结构并且大小不会经常改变,我会明确声明这个值:

pub const FOO_SIZE: usize = 100;

and then declare a function that will fail to compile if the hard-coded constant is wrong:然后声明一个 function 如果硬编码常量错误则编译失败:

fn _assert_foo_size(s: &mut the_struct) {
    s.foo = [0; FOO_SIZE];
}

On the nightly channel I came up with this:在夜间频道上,我想出了这个:

#![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);
}

As far as I can tell, &raw const (*null).foo is not UB because dereferencing a null-pointer to get another pointer is explicitly allowed.据我所知, &raw const (*null).foo不是 UB,因为明确允许取消引用空指针以获取另一个指针。 Unfortunately not only does this require the still unstable raw_ref_op feature, but because this dereferences a pointer, this also cannot be const .不幸的是,这不仅需要仍然不稳定的raw_ref_op功能,而且因为 this 取消引用了一个指针,所以 this 也不能是const

This works in stable, and in const contexts starting in 1.58.这适用于从 1.58 开始的稳定和const上下文。

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

The addr_of! addr_of! macro is stable and works with raw pointers into MaybeUninit<T> , but not null pointers.宏是稳定的,可以使用指向MaybeUninit<T>的原始指针,但不能使用 null 指针。

You can use closures to "simulate" evaluation of my_struct::foo without constructing it with the following:您可以使用闭包来“模拟” 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>()
}

Playground 操场

This just makes rust infer the return type of the closure, U = [c_char; 100]这只是让 rust 推断出闭包的返回类型, U = [c_char; 100] U = [c_char; 100] given a fn(the_struct) -> U , and returns it's size. U = [c_char; 100]给定一个fn(the_struct) -> U ,并返回它的大小。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM