【问题标题】:How do I determine the size of an array at compile time in Rust?如何在 Rust 编译时确定数组的大小?
【发布时间】:2020-02-22 21:19:03
【问题描述】:

我有一个 C 库,它需要明确定义字符串长度的字符串类型:

#[repr(C)]
pub struct FFIStr {
    len: usize,
    data: *const u8,
}

因为这种类型用作静态类型,所以我想要一种使用 const 函数或宏安全地声明它的方法(而不是手动设置 len)。

我的第一次尝试是使用宏和len(),但是在versions before 1.39.0 中,无法获得length of a slice as a const fn

macro_rules! ffi_string {
    ($x:expr) => {
        FFIStr { len: $x.len(), data: $x as *const u8 }
    };
}

#[no_mangle]
pub static mut HELLO_WORLD: FFIStr = ffi_string!(b"Hello, world!");

error: core::slice::<impl [T]>::len` is not yet stable as a const function

我的第二次尝试是使用std::mem::size_of<T>,但似乎没有办法在不使用泛型的情况下获取静态数组的类型:

const fn ffi_string<T>(s: &'static T) -> FFIStr {
    FFIStr { len: ::std::mem::size_of::<T>(), data: s as *const _ as *const _ }
}

#[no_mangle]
pub static mut HELLO_WORLD: FFIStr = ffi_string(b"Hello, world!");

虽然这很有效(令人惊讶),但它极易被滥用,因为它会疯狂地将你传递给*const u8 的任何内容都转换为*const u8

const_generics 似乎是一个很好的解决方案,但它们目前不稳定:

const fn ffi_string<const SIZE: usize>(s: &'static [u8; SIZE]) -> FFIStr {
    FFIStr { len: SIZE, data: s as *const u8 }
}

#[no_mangle]
pub static mut X: FFIStr = ffi_string(b"Hello, world!");

error[E0658]: const generics are unstable

有没有更好的方法在编译时确定静态数组的大小?

【问题讨论】:

    标签: arrays static rust compile-time-constant


    【解决方案1】:

    在 Rust 1.39.0 中,[T]::lenstabilised as a const function,现在直截了当:

    const ARRAY: [i32; 3] = [1, 2, 3];
    const ARRAY_SIZE: usize = ARRAY.len();
    
    fn main() {
        assert_eq!(3, ARRAY_SIZE);
    }
    

    在早期版本的 Rust 中,这是一种基于通用 C ARRAY_SIZE 宏的方法:

    macro_rules! array_size {
        ($x:expr) => (
            (size_of_val($x) / size_of_val(&$x[0]))
        )
    }
    
    const fn size_of_val<T>(_: &T) -> usize {
        std::mem::size_of::<T>()
    }
    
    fn main() {
        assert_eq!(3, array_size!(&[1, 2, 3]));
        assert_eq!(13, array_size!(b"Hello, world!"));
    }
    

    它使用 const 泛型函数 size_of_val&lt;T&gt; 来确定类型,从而确定通过引用传递的值的大小(内置 std::mem::size_of_val 不是 const)。

    注意:这不适用于大小为 0 的数组。这可以通过使用 size_of_val($x) / size_of_val(unsafe { &amp;*$x.as_ptr() }) 来解决,但代价是错误地接受了非数组类型(例如 &amp;String)。

    【讨论】:

    • @trentcl 不幸的是mem::size_of_val 不是一个常量函数,所以不能用于常量或静态值(但基本上是同一个函数)。
    • size_of_val(unsafe { &amp;*$x.as_ptr() }) 是一个坏主意的另一个原因是,对于一个零大小的数组,*$x.as_ptr() 在技术上取消引用一个无效指针(因为第零个元素不存在)。
    • 现在[T]::lenRust 1.39.0 中的一个常量函数,这可能可以简化。
    猜你喜欢
    • 2014-11-28
    • 2013-11-15
    • 2016-06-18
    • 1970-01-01
    • 1970-01-01
    • 2020-08-15
    • 2011-03-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多