Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

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, i.e.:

C code:

#define FOO_SIZE 100

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

Generated FFI bindings:

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. I also do not want to hard code FOO_SIZE in my Rust API, since it is subject to change.

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. 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? If not, what would be the best approach? Changing C code is not an option.

like image 377
Roman Dmitrienko Avatar asked Apr 05 '20 16:04

Roman Dmitrienko


1 Answers

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. Unfortunately not only does this require the still unstable raw_ref_op feature, but because this dereferences a pointer, this also cannot be const.

like image 110
mcarton Avatar answered Nov 14 '22 02:11

mcarton