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

20jt8wwn  于 12个月前  发布在  其他
关注(0)|答案(8)|浏览(86)

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

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

字符串
我该怎么做?

qoefvg9y

qoefvg9y1#

你可以很容易地用TryInto trait(在Rust 1.34中稳定了)做到这一点:

// Before Rust 2021, you need to import the trait:
// use std::convert::TryInto;

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

字符串
但更好的是:没有必要克隆/复制你的元素!实际上可以从&[u8]得到&[u8; 3]

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


正如在其他答案中提到的,如果barry的长度不是3,您可能不想惊慌,而是优雅地处理这个错误。
这要归功于相关trait TryFrom的这些impls(在Rust 1.47之前,这些只存在于长度为32的数组中):

impl<'_, T, const N: usize> TryFrom<&'_ [T]> for [T; N]
where
    T: Copy, 

impl<'a, T, const N: usize> TryFrom<&'a [T]> for &'a [T; N]

impl<'a, T, const N: usize> TryFrom<&'a mut [T]> for &'a mut [T; N]

5q4ezhmt

5q4ezhmt2#

感谢@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!

dddzy1tm

dddzy1tm3#

我推荐使用crate arrayref,它有一个方便的宏来完成这个任务。
注意,使用这个crate,您创建了一个数组&[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()
}

g6ll5ycj

g6ll5ycj4#

您可以手动建立数组并传回它。
如果你想得到多于(或少于)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
}

字符串

wztqucjr

wztqucjr5#

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

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]])
    }
}

oknrviil

oknrviil6#

我对其他答案不满意,因为我需要几个函数返回不同长度的固定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>

字符串

0ve6wy6x

0ve6wy6x7#

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);

字符串


的数据
这是因为数组是一维数组。
要一起测试vec,slice和array,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);



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

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);


cig3rfwq

cig3rfwq8#

值得注意的是,如果数组是字符串,例如。如果你想转换&'static str -> [u32; N],那么有时候你可以将原始字符串初始化为b"my string"。Rust中的b""语法创建静态已知大小的字节数组(例如&[u8, 32])。

相关问题