How to get a slice as an array in Rust?

2019-01-03 10:41发布

I have an array of an unknown size, and I would like to get a slice of that array and convert it to a statically sized array:

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

How would I do this?

标签: arrays rust
6条回答
我只想做你的唯一
2楼-- · 2019-01-03 10:55

I recommend using the crate arrayref, which has a handy macro for doing just this.

Note that, using this crate, you create a reference to an array, &[u8; 3], because it doesn't clone any data!

If you do want to clone the data, then you can still use the macro, but call clone at the end:

#[macro_use]
extern crate arrayref;

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

or

#[macro_use]
extern crate arrayref;

fn pop(barry: &[u8]) -> [u8; 3] {
    array_ref!(barry, 0, 3).clone()
}
查看更多
Viruses.
3楼-- · 2019-01-03 11:08

Here's a function that matches the type signature you asked for.

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

But since barry could have fewer than three elements, you may want to return an Option<[u8; 3]> rather than a [u8; 3].

fn pop(barry: &[u8]) -> Option<[u8; 3]> {
    if barry.len() < 3 {
        None
    } else {
        Some([barry[0], barry[1], barry[2]])
    }
}
查看更多
不美不萌又怎样
4楼-- · 2019-01-03 11:11

This answer uses the unstable try_from feature!


You can easily do this with the brand new TryInto trait (which is still unstable as of June 2018):

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

But even better: there is no need to clone your array! It is actually possible to get a &[u8; 3] from a &[u8]:

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

As mentioned in the other answers, you probably don't want to panic if the length of barry is not 3, so you should return an Option<[u8; 3]> or something similar to handle this error gracefully.

This works thanks to these impls (where $N is just an integer between 1 and 32) of the related trait TryFrom:

impl<'a, T> TryFrom<&'a [T]> for &'a [T; $N]
  type Error = TryFromSliceError;
查看更多
霸刀☆藐视天下
5楼-- · 2019-01-03 11:12

You can create a custom macro to solve the issue. The constraint here, you have to specify slice size as constant. You can not create array with dynamic length which is explained on these questions:

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

For working code: playground

查看更多
手持菜刀,她持情操
6楼-- · 2019-01-03 11:13

Thanks to @malbarbo we can use this helper function:

use std::convert::AsMut;

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

to get a much neater syntax:

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

as long as T: Default + Clone.

If you know your type implements Copy, you can use this form:

use std::convert::AsMut;

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

Both variants will panic! if the target array and the passed-in slice do not have the same length.

查看更多
女痞
7楼-- · 2019-01-03 11:14

You can manually create the array and return it.

Here is a function that can easily scale if you want to get more (or less) than 3 elements.

Note that if the slice is too small, the end terms of the array will be 0's.

fn pop(barry: &[u8]) -> [u8; 3] {
    let mut array = [0u8; 3];
    for (&x, p) in barry.iter().zip(array.iter_mut()) {
        *p = x;
    }
    array
}
查看更多
登录 后发表回答