Arrays 如何避免重复从现有阵列构造固定大小的阵列?

Arrays 如何避免重复从现有阵列构造固定大小的阵列?,arrays,rust,Arrays,Rust,从现有阵列创建固定大小的阵列时,读写操作可能会非常繁琐,例如: let foo: [i32; 8] = [ bar[1], bar[2], bar[3], bar[4], taz[1], taz[2], taz[3], taz[4], ]; 除了使用for循环分配值之外,Rust是否提供了一种无需手动扩展数组即可写入值的方法 例如,类似于: 注意:现实世界的示例使用了更多的项,只是在示例中保持简短 使用向量这是可能的,但是从固定大小的数组移动到向量并不是免费的(经过测试,它在

从现有阵列创建固定大小的阵列时,读写操作可能会非常繁琐,例如:

let foo: [i32; 8] = [
    bar[1], bar[2], bar[3], bar[4],
    taz[1], taz[2], taz[3], taz[4],
];
除了使用for循环分配值之外,Rust是否提供了一种无需手动扩展数组即可写入值的方法

例如,类似于:

注意:现实世界的示例使用了更多的项,只是在示例中保持简短


使用向量这是可能的,但是从固定大小的数组移动到向量并不是免费的(经过测试,它在发布模式下生成了相当多的程序集,按照编写的方式执行转换,使用堆内存,而简化的版本只需要堆栈内存)

让foo:Vec=bar[1..5].iter().chain(taz[1..5].iter()).cloned().collect();

如果您不想进入不安全的代码,我将创建一个
mut-Vec
,然后使用
extend\u from\u slice()
使用切片增量扩展它:

fn main() {
    let bar = [1,2,3,4,5,6];
    let taz = [7,8,9,10,11,12];
    let mut foo = Vec::new();
    foo.extend_from_slice(&bar[1..5]);
    foo.extend_from_slice(&taz[1..5]);
}
之后,可以使用
将其转换为固定长度的切片,并将其转换为\u装箱的\u切片()
。或者,如果需要数组,可以使用我在以下中找到的函数:


出于好奇,这里有一个支持解包的数组连接宏(限于有限数量的大小,这个宏很小,可以扩展)

与任何其他解决方案相比,我对该解决方案的性能或零成本没有任何要求(请参考资料)。如果一直使用固定大小的数组,则所有边界检查都会在编译时进行

第一个使用示例:

fn main() {
    let data = [0, 1, 2, 3, 4, 5, 6, 7, 8];

    println!("{:?}", concat_arrays!([1, 2, 3] [4] [5, 6]));
    // [1, 2, 3, 4, 5, 6]

    println!("{:?}", concat_arrays!(data[3..];3 [-1] data;3));
    // [3, 4, 5, -1, 0, 1, 2]

    // let's look at the macro expansion of the last one
    println!("{}", concat_arrays!(@build stringify [] data[3..];3 [-1] data;3));
    // [ data[3..][0] , data[3..][1] , data[3..][2] , -1 , data[0] , data[1] , data[2] ]
}
然后实施:

根据你的问题,我会这样做:

concat_数组!(条形[1..];4塔兹[1..];4)

这是简洁的,但确实存在一些问题,例如语法与特定宏的特殊性,以及源于
4
的问题必须是一个文本,不仅如此,还必须是有限列表中的文本


编辑:


请参阅,包括用于生成支持预定义数量的参数的宏的脚本,这些参数可能比此处给出的示例大得多。

在本示例中,
foo
是一个
Vec
,而不是一个固定大小的数组。因为问题是关于固定大小的数组,(
[i32;8]
),这个答案可以通过演示如何从
Vec
获取
[i32;8]
来完成。这似乎太复杂了,对于性能关键型代码来说,创建多个中间数组只是为了节省一些输入,这不一定是合理的。我想知道这是否可以用零开销表示。(通过宏或其他方式展开)。@ideasman42由于宏不计算表达式的限制,恐怕这是不可能的。例如,宏甚至不能在编译时计数。但是如果插件是一种选择,那么这将成为可能。在代码中创建数组与在实际编译程序中创建中间数组的程序不一致。编译器知道如何将复合值粉碎成标量并仔细研究它们。crate
matrixmultiply
的内核使用中间数组,在编译后的代码中它实际上代表simd寄存器。它在发布模式下生成了相当多的程序集,这并不意味着什么。代码越长,速度就越快,这并不少见;例如,可能展开了一个循环。**评测**说什么更快?一般来说,在假设生成的程序集更好/更差之前,先同意评测是最好的。通过阅读这两种情况下的ASM,可以清楚地看到,这段代码的矢量版本没有优化中间步骤,实际上是在创建多个内部数据结构并对其进行转换。这与已经手动展开的函数的简单版本相比。这里不需要使用堆内存,如果这是一个C程序,它就像在不需要时使用多个malloc和memcpy一样。
fn main() {
    let bar = [1,2,3,4,5,6];
    let taz = [7,8,9,10,11,12];
    let mut foo = Vec::new();
    foo.extend_from_slice(&bar[1..5]);
    foo.extend_from_slice(&taz[1..5]);
}
use std::convert::AsMut;

fn clone_into_array<A, T>(slice: &[T]) -> A
    where A: Sized + Default + AsMut<[T]>,
          T: Clone
{
    let mut a = Default::default();
    <A as AsMut<[T]>>::as_mut(&mut a).clone_from_slice(slice);
    a
}
let fixed: [i32; 8] = clone_into_array(&foo);
fn main() {
    let data = [0, 1, 2, 3, 4, 5, 6, 7, 8];

    println!("{:?}", concat_arrays!([1, 2, 3] [4] [5, 6]));
    // [1, 2, 3, 4, 5, 6]

    println!("{:?}", concat_arrays!(data[3..];3 [-1] data;3));
    // [3, 4, 5, -1, 0, 1, 2]

    // let's look at the macro expansion of the last one
    println!("{}", concat_arrays!(@build stringify [] data[3..];3 [-1] data;3));
    // [ data[3..][0] , data[3..][1] , data[3..][2] , -1 , data[0] , data[1] , data[2] ]
}
/// Concatenate array literals and fixed-size unpacked parts of arrays and slices
///
/// Usage: `concat_arrays!(fragments)`  
/// where each fragment is either an array literal: `[x, y, z]`  
/// or an expression and how many elements to unpack: `expression;N`  
/// where `N` must be an integer literal
///
/// See: https://gitlab.com/snippets/27095
/// for a script to generate a macro supporting many more arguments.
macro_rules! concat_arrays {
    // last step -> build an expression
    (@build as_expr [$($t:expr),*]) => {
        [$($t),*]
    };
    (@build $m:ident [$($t:expr),*]) => {
        $m!([$($t),*])
    };
    (@build $m:ident [$($t:expr),*] [$($e:expr),+] $($more:tt)*) => {
        concat_arrays!(@build $m [$($t,)* $($e),*] $($more)*)
    };
    (@build $m:ident [$($t:expr),*] $e:expr;1 $($more:tt)*) => {
        concat_arrays!(@build $m [$($t,)* $e[0]] $($more)*)
    };
    (@build $m:ident [$($t:expr),*] $e:expr;2 $($more:tt)*) => {
        concat_arrays!(@build $m [$($t,)* $e[0], $e[1]] $($more)*)
    };
    (@build $m:ident [$($t:expr),*] $e:expr;3 $($more:tt)*) => {
        concat_arrays!(@build $m [$($t,)* $e[0], $e[1], $e[2]] $($more)*)
    };
    (@build $m:ident [$($t:expr),*] $e:expr;4 $($more:tt)*) => {
        concat_arrays!(@build $m [$($t,)* $e[0], $e[1], $e[2], $e[3]] $($more)*)
    };
    (@build $m:ident [$($t:expr),*] $e:expr;5 $($more:tt)*) => {
        concat_arrays!(@build $m [$($t,)* $e[0], $e[1], $e[2], $e[3], $e[4]] $($more)*)
    };

    // user facing case
    ($($t:tt)+) => {
        concat_arrays!(@build as_expr [] $($t)+)
    }
}