如何在不实例化 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;
然后声明一个如果硬编码常量错误将无法编译的函数:
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
功能,而且因为这取消引用了一个指针,所以这也不能是 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>
的原始指针,但不能使用空指针。
您可以使用闭包来“模拟”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 推断闭包的 return 类型,U = [c_char; 100]
给定 fn(the_struct) -> U
,并且 return 它的大小。
我有一个包含字节数组的结构。这个结构实际上来自于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;
然后声明一个如果硬编码常量错误将无法编译的函数:
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
功能,而且因为这取消引用了一个指针,所以这也不能是 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>
的原始指针,但不能使用空指针。
您可以使用闭包来“模拟”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 推断闭包的 return 类型,U = [c_char; 100]
给定 fn(the_struct) -> U
,并且 return 它的大小。