如何在Rust中将切片作为数组?

时间:2014-08-21 14:25:39

标签: arrays rust

我有一个未知大小的数组,我想获得该数组的一部分并将其转换为静态大小的数组:

fn pop(barry: &[u8]) -> [u8; 3] {
    barry[0..3] // mismatched types: expected `[u8, ..3]` but found `&[u8]`
}

我该怎么做?

8 个答案:

答案 0 :(得分:17)

感谢@malbarbo我们可以使用这个辅助函数:

use std::convert::AsMut;

fn clone_into_array<A, T>(slice: &[T]) -> A
where
    A: Default + AsMut<[T]>,
    T: Clone,
{
    let mut a = A::default();
    <A as AsMut<[T]>>::as_mut(&mut a).clone_from_slice(slice);
    a
}

获得更整洁的语法:

fn main() {
    let original = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

    let e = Example {
        a: clone_into_array(&original[0..4]),
        b: clone_into_array(&original[4..10]),
    };

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

只要T: Default + Clone

如果您知道您的类型实现Copy,则可以使用以下格式:

use std::convert::AsMut;

fn copy_into_array<A, T>(slice: &[T]) -> A
where
    A: Default + AsMut<[T]>,
    T: Copy,
{
    let mut a = A::default();
    <A as AsMut<[T]>>::as_mut(&mut a).copy_from_slice(slice);
    a
}

如果目标数组和传入的切片长度不同,则两个变体都将panic!

答案 1 :(得分:15)

这是一个与您要求的类型签名相匹配的函数。

fn pop(barry: &[u8]) -> [u8; 3] {
    [barry[0], barry[1], barry[2]]
}

但由于barry可能少于三个元素,因此您可能希望返回Option<[u8; 3]>而不是[u8; 3]

fn pop(barry: &[u8]) -> Option<[u8; 3]> {
    if barry.len() < 3 {
        None
    } else {
        Some([barry[0], barry[1], barry[2]])
    }
}

答案 2 :(得分:14)

您可以使用TryInto特征(在Rust 1.34中稳定)轻松完成此操作:

fn pop(barry: &[u8]) -> [u8; 3] {
    barry.try_into()
        .map(|s: &[u8; 3]| s.clone())
        .expect("slice with incorrect length")
}

但更好的是:没有必要克隆你的阵列!实际上可以从&[u8; 3]获得&[u8]

fn pop(barry: &[u8]) -> &[u8; 3] {
    barry.try_into().expect("slice with incorrect length")
}

正如其他答案中所提到的,如果barry的长度不是3,你可能不想恐慌,所以你应该返回一个Option<[u8; 3]>或类似的东西来优雅地处理这个错误。

这要归功于相关特征TryFrom的这些impl(其中$N只是1到32之间的整数):

impl<'a, T> TryFrom<&'a [T]> for &'a [T; $N]
  type Error = TryFromSliceError;

答案 3 :(得分:13)

我建议使用crate arrayref,它有一个方便的宏来做这件事。

请注意,使用此包,您可以创建对数组&[u8; 3]的引用,因为它不会克隆任何数据!

如果你想要克隆数据,那么你仍然可以使用宏,但最后调用clone:

#[macro_use]
extern crate arrayref;

fn pop(barry: &[u8]) -> &[u8; 3] {
    array_ref!(barry, 0, 3)
}

#[macro_use]
extern crate arrayref;

fn pop(barry: &[u8]) -> [u8; 3] {
    array_ref!(barry, 0, 3).clone()
}

答案 4 :(得分:10)

您可以手动创建阵列并将其返回。

如果你想得到比3个元素更多(或更少)的元素,这个函数可以轻松扩展。

请注意,如果切片太小,则数组的结束项将为0。

fn pop(barry: &[u8]) -> [u8; 3] {
    let mut array = [0u8; 3];
    for (&x, p) in barry.iter().zip(array.iter_mut()) {
        *p = x;
    }
    array
}

答案 5 :(得分:1)

Vec,'Slice'和Array之间的共同点是Iter,因此您可以将zipmap一起使用,就像:

let x = vec![1, 2, 3];
let mut y: [u8; 3] = [Default::default(); 3];
println!("y at startup: {:?}", y);    
x.iter().zip(y.iter_mut()).map(|(&x, y)| *y = x).count();
println!("y copied from vec: {:?}", y);

enter image description here

这是因为数组是一维数组。

要一起测试vec,切片和阵列,请here进行测试:

let a = [1, 2, 3, 4, 5];
let slice = &a[1..4];
let mut x: Vec<u8> = vec![Default::default(); 3];
println!("X at startup: {:?}", x);    
slice.iter().zip(x.iter_mut()).map(|(&s, x)| *x = s).count();
println!("X copied from vec: {:?}", x);

enter image description here

另一个应该比逐字节复制更快的选项是:

y[..x.len()].copy_from_slice(&x);

这适用于所有人,下面是示例:

let a = [1, 2, 3, 4, 5];
let mut b: Vec<u8> = vec![Default::default(); 5];
b[..a.len()].copy_from_slice(&a);
println!("Copy array a into vector b: {:?}", b);

let x: Vec<u8> = vec![1, 2, 3, 4, 5];
let mut y: [u8; 5] = [Default::default(); 5];
y[..x.len()].copy_from_slice(&x);
println!("Copy vector x into array y: {:?}", y);

enter image description here

答案 6 :(得分:0)

您可以创建一个自定义宏来解决此问题。 在这里的约束,您必须将切片大小指定为常量。您无法创建具有动态长度的数组,对此问题进行了解释:

  

How to set a Rust array length dynamically?

     

Is it possible to have stack allocated arrays with the size determined at runtime in Rust?

macro_rules! slice_to_array {
    ($x:expr, $size:expr) => {{
        let mut array = [0; $size];
        array.copy_from_slice(&$x[..$size]);
        array
    }};
}

fn main() {
    let vec = vec![1i64, 2, 3, 4, 5, 7];
    let array = slice_to_array!(vec, 4); // It works fine
    // Attempt to use non-constant value in a constant
    // let size = 4;
    // let array = slice_to_array!(vec, size); 
    println!("{:?}", array);
}

有关工作代码:playground

答案 7 :(得分:0)

我对其他答案不满意,因为我需要几个函数来返回可变长度的固定u8数组。我编写了一个宏,该宏生成特定于任务的功能。希望对别人有帮助。

#[macro_export]
macro_rules! vec_arr_func {
    ($name:ident, $type:ty, $size:expr) => {
        pub fn $name(data: std::vec::Vec<$type>) -> [$type; $size] {
            let mut arr = [0; $size];
            arr.copy_from_slice(&data[0..$size]);
            arr
        }
    };
}

//usage - pass in a name for the fn, type of array, length
vec_arr_func!(v32, u8, 32);
v32(data); //where data is std::vec::Vec<u8>