Как получить размер структурного поля в Rust без его создания - PullRequest
1 голос
/ 05 апреля 2020

У меня есть структура с байтовым массивом. Эта структура фактически происходит из привязок FFI, созданных bindgen, и ее размер определяется в C коде с использованием макроса, например:

C code:

#define FOO_SIZE 100

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

Сгенерированный FFI bindings:

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

Я хочу убедиться, что данные, поступающие со стороны Rust API, вписываются в foo. Я также не хочу жестко кодировать FOO_SIZE в моем Rust API, поскольку он может быть изменен.

Я понимаю, что это можно сделать, создав сначала экземпляр структуры, но потом снова, что потребует явного инициализация foo, которая кажется невозможной без знания ее размера. Кроме того, это дополнительный шаг, который я хочу избежать.

Можно ли каким-то образом получить размер foo статически, не создавая структуру? Если нет, то какой будет лучший подход? Изменение C кода не является возможным.

Ответы [ 2 ]

2 голосов
/ 05 апреля 2020

На ночном канале я придумал следующее:

#![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, но и потому, что это разыменовывает указатель, также не может быть const.

2 голосов
/ 05 апреля 2020

Я не знаю, возможно ли пока получить размер массива, но если у вас не так много таких структур и размер не меняется слишком часто, я просто объявил бы значение явно:

pub const FOO_SIZE: usize = 100;

и затем объявляем функцию, которая не будет компилироваться, если жестко заданная константа неверна:

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