const 泛型数组大小的算术推断 conditions/constraints

Arithmetic inferral conditions/constraints for const generic arraysize

我有这个函数将一个向量分成两个奇数元素和偶数元素的向量:

pub fn odd_even<T>(x: Vec<T>, upto: usize) -> (Vec<T>, Vec<T>)
where T: Copy
{
    let mut odd = Vec::new();
    let mut even = Vec::new();
    let oddeven: [&mut Vec<T>; 2] = [&mut odd, &mut even];
    for n in 0..upto
    {
        oddeven[1 - n%2].push(x[n]);
    }
    return (odd, even);
}

这工作正常并且适用于动态大小的向量。

但是,我正在考虑为此使用 const 通用长度数组。下面的代码为 LEN_ODDLEN_EVEN 的约束(或者你怎么称呼它?)生成编译器错误,因为 LEN 是一个常量,但它应该传达这个想法。我什至不确定这种语言是否允许我做这种事情,但我想知道这是否可能。

有没有办法说 LEN_ODDLEN_EVEN 必须对应于 LEN 的某个值,以便可以自动推断出这两个常数的值由编译器?也许这是一个愚蠢的想法,但我想尝试一下。该代码将用于实现快速傅里叶变换,其中性能至关重要。或许宏会是更好的实现方式?

fn odd_even<T, const LEN: usize, const LEN_ODD: usize, const LEN_EVEN: usize>
(x: [T; LEN]) -> ([T; LEN_ODD], [T; LEN_EVEN])
where
T: Default, T: Copy,
LEN_ODD == LEN << 1, // this line is invalid
LEN_EVEN == (LEN + 1) << 1 // this line is also invalid
{
    let mut odd: [T; LEN_ODD] = [Default::default(); LEN_ODD];
    let mut even: [T; LEN_EVEN] = [Default::default(); LEN_EVEN];

    let oddeven: [&mut [T]; 2] = [&mut odd, &mut even];
    for n in 0..LEN
    {
        oddeven[1 - n%2][n << 1] = x[n];
    }
    return (odd, even);
}

我是 Rust 的新手,对显式使用指针的语言没有太多经验。那里可能有一个解决方案,我完全错过了引用数组的第一个元素,但是数组的长度不会受到编译器的限制,这很容易让人困惑。到时候你还不如用向量对吧?

使用断言,编译器仍然不会推断 LEN_ODDLEN_EVEN:

fn odd_even<T, const LEN: usize, const LEN_ODD: usize, const LEN_EVEN: usize>
(x: [T; LEN]) -> ([T; LEN_ODD], [T; LEN_EVEN])
where
T: Default, T: Copy
{
    assert_eq!(LEN_ODD, LEN << 1);
    assert_eq!(LEN_EVEN, (LEN + 1) << 1);

    let mut odd: [T; LEN_ODD] = [Default::default(); LEN_ODD];
    let mut even: [T; LEN_EVEN] = [Default::default(); LEN_EVEN];

    let oddeven: [&mut [T]; 2] = [&mut odd, &mut even];
    for n in 0..LEN
    {
        oddeven[1 - n%2][n << 1] = x[n];
    }
    return (odd, even);
}

上面的最终函数可以这样成功调用:

odd_even::<usize, 4, 2, 2>([0, 2, 3, 5]); // -> [2, 5], [0, 3]

但是长度必须指定,不会被推断。我正在寻找的是一种告诉编译器如何推断它们的方法。

对 const 泛型进行算术运算需要 generic_const_exprs 特性。这是启用了该功能的代码的有效实现:

#![feature(generic_const_exprs)]
#![allow(incomplete_features)]

fn main() {
    let _: ([u32; 2], [u32; 2]) = dbg!(odd_even([0, 2, 3, 5]));
    let _: ([u32; 2], [u32; 3]) = dbg!(odd_even([0, 2, 3, 5, 6]));
}

fn odd_even<T, const LEN: usize>(x: [T; LEN]) -> ([T; LEN / 2], [T; (LEN + 1) / 2])
where
    T: Default,
    T: Copy,
{
    let mut odd = [Default::default(); LEN / 2];
    let mut even = [Default::default(); (LEN + 1) / 2];

    let oddeven: [&mut [T]; 2] = [&mut odd, &mut even];

    for n in 0..LEN {
        oddeven[1 - n % 2][n / 2] = x[n];
    }

    return (odd, even);
}

输出:

[src/main.rs:5] odd_even([0, 2, 3, 5]) = (
    [
        2,
        5,
    ],
    [
        0,
        3,
    ],
)
[src/main.rs:6] odd_even([0, 2, 3, 5, 6]) = (
    [
        2,
        5,
    ],
    [
        0,
        3,
        6,
    ],
)

Playground