如何在 Rust 中表示仅具有特定大小的数组

RBF*_*F06 1 arrays generics rust const-generics

我想要一个表示值数组的数据结构,但它仅支持一组特定的大小并在编译时强制执行。就像是:

struct MyArray<const N: usize>([u8; N]);
Run Code Online (Sandbox Code Playgroud)

但这样的N只能是一组特定的值,而不仅仅是 可以表示的任何数字usize。例如,我希望一个结构体可以包装 a [u8; 3]、 a[u8; 6]或 a [u8; 9],但不能包装除3、6 和 9 之外的[u8; N]任何其他结构体。我需要在编译时强制执行此约束,并且最好是N类型系统。这在 Rust 中可能吗?是否有执行此操作的标准模式或板条箱?

dre*_*ato 5

您可以通过仅提供所需长度的构造函数来做到这一点。

impl MyArray<3> {
    pub fn new(arr: [u8; 3]) -> Self {
        Self(arr)
    }
}

impl MyArray<6> {
    pub fn new(arr: [u8; 6]) -> Self {
        Self(arr)
    }
}

impl MyArray<9> {
    pub fn new(arr: [u8; 9]) -> Self {
        Self(arr)
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,在包含 的模块之外MyArray,您只能创建MyArray长度为 3、6 或 9 的值。

如果您有更多数字,宏会有所帮助。

macro_rules! impl_my_array {
    ($($n:expr),* $(,)?) => {$(
        impl MyArray<$n> {
            pub fn new(arr: [u8; $n]) -> Self {
                Self(arr)
            }
        }
    )*}
}

impl_my_array! {
    3, 6, 9
}
Run Code Online (Sandbox Code Playgroud)

这些的缺点是类型推断无法确定new要调用哪个,因此每当构造值时都需要指定 const 泛型。

//                 V
let ma = MyArray::<3>::new([1, 2, 3]);
Run Code Online (Sandbox Code Playgroud)

您可能想给它们不同的名称,或者提供一个更高级别的构造函数来选择特定的名称new或返回错误。


如果您需要大量数字,例如跨越整个usize范围,则需要使用该const技巧。

这个允许每一个都usize被 3 整除。

impl<const N: usize> MyArray<N> {
    const LEN_IS_DIVISIBLE_BY_3: () = if N % 3 != 0 {
        panic!("MyArray values must have length divisible by 3");
    };
    pub fn new(arr: [u8; N]) -> Self {
        let _ = Self::LEN_IS_DIVISIBLE_BY_3;
        Self(arr)
    }
}
Run Code Online (Sandbox Code Playgroud)

这在此处此处的bytemuck 箱中使用。