如何连接已知长度的数组?

问题描述 投票:0回答:6

我有两个已知长度的数组:

let left: [u8; 2] = [1, 2];
let right: [u8; 3] = [3, 4, 5];

我的第一次尝试:

let whole: [u8; 5] = left + right;

失败并出现错误:

error[E0369]: cannot add `[u8; 2]` to `[u8; 3]`
  --> /home/fadedbee/test.rs:25:29
   |
25 |         let whole: [u8; 5] = left + right;
   |                              ---- ^ ----- [u8; 3]
   |                              |
   |                              [u8; 2]

同样:

let whole: [u8; 5] = left.concat(right);

失败:

error[E0599]: the method `concat` exists for array `[u8; 2]`, but its trait bounds were not satisfied
  --> /home/fadedbee/test.rs:25:29
   |
25 |         let whole: [u8; 5] = left.concat(right);
   |                                   ^^^^^^ method cannot be called on `[u8; 2]` due to unsatisfied trait bounds
   |
   = note: the following trait bounds were not satisfied:
           `<[u8] as std::slice::Concat<_>>::Output = _`

我目前正在使用以下形式的表达式:

let whole: [u8; 5] = [left[0], left[1], right[0], right[1], right[2]];

但是对于我的实际用例来说,这是几十个元素,很容易出现错别字。

@Emoun 友善地指出我误用了

concat
.

正确尝试:

 let whole: [u8; 5] = [left, right].concat();

我得到:

error[E0308]: mismatched types
  --> /home/fadedbee/test.rs:32:31
   |
32 |         let whole: [u8; 5] = [left, right].concat();
   |                                     ^^^^^ expected an array with a fixed size of 2 elements, found one with 3 elements
   |
   = note: expected type `[u8; 2]`
             found array `[u8; 3]`

如何将已知长度的数组连接成固定长度的数组?

arrays rust concatenation
6个回答
12
投票

我想有更好的答案,但你可以这样做:

fn main() {
    let left: [u8; 2] = [1, 2];
    let right: [u8; 3] = [3, 4, 5];

    let whole: [u8; 5] = {
        let mut whole: [u8; 5] = [0; 5];
        let (one, two) = whole.split_at_mut(left.len());
        one.copy_from_slice(&left);
        two.copy_from_slice(&right);
        whole
    };

    println!("{:?}", whole);
}

6
投票

如果想避免初始化目标数组的开销,可以使用

MaybeUninit()
和不安全的代码:

let a = [1, 2, 3];
let b = [4, 5];
let concatenated: [u8; 5] = unsafe {
    let mut result = std::mem::MaybeUninit::uninit();
    let dest = result.as_mut_ptr() as *mut u8;
    std::ptr::copy_nonoverlapping(a.as_ptr(), dest, a.len());
    std::ptr::copy_nonoverlapping(b.as_ptr(), dest.add(a.len()), b.len());
    result.assume_init()
};

一旦 Rust 完全支持 const 泛型,就可以将这段代码放在一个函数中。在 Nightly 上,今天已经可以使用

generic_const_expr
功能:

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

pub fn concat_arrays<T, const M: usize, const N: usize>(a: [T; M], b: [T; N]) -> [T; M + N] {
    let mut result = std::mem::MaybeUninit::uninit();
    let dest = result.as_mut_ptr() as *mut T;
    unsafe {
        std::ptr::copy_nonoverlapping(a.as_ptr(), dest, M);
        std::ptr::copy_nonoverlapping(b.as_ptr(), dest.add(M), N);
        std::mem::forget(a);
        std::mem::forget(b);
        result.assume_init()
    }
}

请注意,我们不需要对

T
做任何假设,因为所有 Rust 类型都可以通过按位复制移动。


3
投票

您对

concat()
方法的使用不太正确。这是您的操作方法:

fn main(){
    let left: [u8; 2] = [1, 2];
    let right: [u8; 2] = [3, 4];
    
    assert_eq!([left, right].concat(), [1,2,3,4]);
}


3
投票

根据@yolenoyer 的回答,这可行,但目前需要

cargo +nightly test
才能启用
const_evaluatable_checked
.

#![feature(const_generics)]
#![feature(const_evaluatable_checked)]

pub fn concat<T: Copy + Default, const A: usize, const B: usize>(a: &[T; A], b: &[T; B]) -> [T; A+B] {
    let mut whole: [T; A+B] = [Default::default(); A+B];
    let (one, two) = whole.split_at_mut(A);
    one.copy_from_slice(a);
    two.copy_from_slice(b);
    whole
}

#[cfg(test)]
mod tests {
    use super::concat;

    #[test]
    fn it_works() {
        let a: [u8; 2] = [1, 2];
        let b: [u8; 3] = [3, 4, 5];

        let c: [u8; 5] = concat(&a, &b);

        assert_eq!(c, [1, 2, 3, 4, 5]);
    }
}

2
投票

你可以试试这个: 基本上你会先得到 vec,然后尝试将它转换成数组。

use std::convert::TryInto;

pub fn array_concat() {
    let left: [u8; 2] = [1, 2];
    let right: [u8; 3] = [3, 4, 5];
    let whole: Vec<u8> = left.iter().copied().chain(right.iter().copied()).collect();

    let _whole: [u8; 5] = whole.try_into().unwrap();
}

注意:

try_into
适用于 1.48 (Vec转数组有什么好办法吗?

编辑:

如果你不想使用

copied()
那么你可以尝试下面的代码片段:

pub fn array_concat3() {
    let a1 = [1, 2, 3];
    let a2 = [4, 5, 6, 7];

    let mut whole: Vec<u8> = a1.iter().chain(a2.iter()).map(|v| *v).collect();
    let whole: [u8; 7] = whole.try_into().unwrap();
    println!("{:?}", whole);
}

0
投票

作为Sven Marnach 的回答 的补充,如果您不介意使用夜间功能(即使是未完成的功能),您可以这样做:

#![feature(generic_const_exprs)]
#![feature(inline_const)]
#![feature(maybe_uninit_array_assume_init)]

use std::mem::MaybeUninit;

fn concat_arrays<T, const M: usize, const N: usize>(a: [T; M], b: [T; N]) -> [T; M + N] {
    let mut result = [const { MaybeUninit::uninit() }; M + N];
    let dest = result.as_mut_ptr() as *mut T;
    unsafe {
        std::ptr::copy_nonoverlapping(a.as_ptr(), dest, M);
        std::ptr::copy_nonoverlapping(b.as_ptr(), dest.add(M), N);
        std::mem::forget(a);
        std::mem::forget(b);
        MaybeUninit::array_assume_init(result)
    }
}

注意这里调用

std::mem::forget
是安全的,因为
a
b
对应的内存已经被移动到
result
中,不再被使用,所以不需要额外的destructioncleanup 当函数退出时(当它们的作用域结束时)。追求极致性能时可以使用此技巧,但
std::mem::forget
通常应谨慎使用,因为它也可用于转移内存所有权(与std::mem::ManuallyDrop相比)。

© www.soinside.com 2019 - 2024. All rights reserved.