簡體   English   中英

如何在不實例化的情況下獲取 Rust 中結構字段的大小

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

我有一個包含字節數組的結構。 這個結構實際上來自於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 我也不想在我的 Rust API 中硬編碼FOO_SIZE ,因為它可能會發生變化。

我知道這可以通過首先實例化結構來完成,但話又說回來,這需要顯式初始化foo ,這在不知道它的大小的情況下似乎是不可能的。 此外,這是我想避免的額外步驟。

是否可以在不實例化結構的情況下以某種方式靜態獲取foo的大小? 如果沒有,最好的方法是什么? 更改 C 代碼不是一種選擇。

我不知道是否有可能獲得數組大小,但如果你沒有太多這樣的結構並且大小不會經常改變,我會明確聲明這個值:

pub const FOO_SIZE: usize = 100;

然后聲明一個 function 如果硬編碼常量錯誤則編譯失敗:

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

在夜間頻道上,我想出了這個:

#![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

這適用於從 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);
}

addr_of! 宏是穩定的,可以使用指向MaybeUninit<T>的原始指針,但不能使用 null 指針。

您可以使用閉包來“模擬” 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] U = [c_char; 100]給定一個fn(the_struct) -> U ,並返回它的大小。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM