129

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] // expected array `[u8; 3]`, found slice `[u8]`
}

How would I do this?

mcarton
  • 27,633
  • 5
  • 85
  • 95
Jeroen
  • 15,257
  • 12
  • 59
  • 102

8 Answers8

177

You can easily do this with the TryInto trait (which was stabilized in 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")
}

But even better: there is no need to clone/copy your elements! 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, but instead handle this error gracefully.

This works thanks to these impls of the related trait TryFrom (before Rust 1.47, these only existed for arrays up to length 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]
Lukas Kalbertodt
  • 79,749
  • 26
  • 255
  • 305
  • 2
    Now `impl<'a, T> TryFrom<&'a [T]> for [T; $N] where T: Copy` has been provided as well (`[T; $N]` vs `&'a [T; $N]`). – updogliu Mar 19 '19 at 02:21
  • 1
    How can this be done if your array is bigger than 32 bytes? – Alvaro Jan 26 '20 at 04:27
  • @Alvaro Check the other answers for this. Until const generics are stable, my answer only works up to the size of 32. – Lukas Kalbertodt Jan 26 '20 at 09:10
  • I ended up copying this from the standard lib for the size I required (luckily it's for a very specific size) – Alvaro Jan 27 '20 at 20:56
  • 1
    Another syntax, from a `Vec`: `<[i32; 2]>::try_from(&vec[..])` – cambunctious Apr 23 '20 at 16:09
  • Are you aware of an unsafe counter-part? E.g. when using `chunks_exact` there is a guarantee. – Jorge Leitao May 06 '21 at 16:56
  • @JorgeLeitao You can probably just use `TryFrom` and put an `unreachable_unchecked` on the error branch. Or use pointer casts on `[T]::as_ptr`. And even though it's still unstable right now, you might be interested in `[T]::array_chunks` which solves this problem much nicer! – Lukas Kalbertodt May 06 '21 at 18:13
21

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 = A::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 = A::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.

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
18

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()
}
paholg
  • 1,910
  • 17
  • 19
15

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]])
    }
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
mwhittaker
  • 1,745
  • 15
  • 18
  • 23
    ‒​​​​​​​​​1​​​ because the generic case isn't answered (e.g. `[u8; 32]`; nobody will repeat acesss by index 32 times) – diralik Jun 02 '19 at 16:09
  • 2
    @diralik And yet there's people who do... Until const generics are stabilized, this is still the best way. Current rust std quite literally has an unstable trait that ensures array length up to 32 and then implements most traits that make sense to be implemented for every single array type up to 32 times... That's just how reality is right now. You can write code generator that outputs 0 to N array code code at will to make it somewhat better, this answer is good and shows how std does it. Or are you just going to say "tough luck, see you in 5 years" for array of size 33? – Yamirui Sep 02 '20 at 11:00
  • 3
    Update: const generics are stabilized ;) – Bobbbay May 24 '21 at 23:45
15

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
}
Levans
  • 14,196
  • 3
  • 49
  • 53
  • 1
    Would prefer a generic function - needing to define functions for each type would get annoying if you ever need to use this for a non `u8`. – ideasman42 Jan 01 '17 at 09:49
3

I was unhappy with other answers because I needed several functions that return varying length fixed u8 arrays. I wrote a macro that produces functions specific for the task. Hope it helps someone.

#[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>
mattdlockyer
  • 6,984
  • 4
  • 40
  • 44
  • 4
    What's *wrong* with the other answers to make you unhappy? What's *better* about this? – Shepmaster May 22 '19 at 14:25
  • What do you mean by "function printing macro"? – Shepmaster May 22 '19 at 14:26
  • By the way, its not idiomatic to use `return` at the end of the function. – Shepmaster May 22 '19 at 14:27
  • 1
    It's needless to accept a `Vec` here: [Why is it discouraged to accept a reference to a String (&String), Vec (&Vec), or Box (&Box) as a function argument?](https://stackoverflow.com/q/40006219/155423) – Shepmaster May 22 '19 at 14:28
  • This is effectively the same code as [this answer](https://stackoverflow.com/a/37682288/155423), except it uses a macro. – Shepmaster May 22 '19 at 14:29
  • 1
    Thanks for all the comments. I'm new to Rust. What was not clear with the other answers is if we could vary the length of the fixed array being returned. I couldn't figure out how to do this with a function so resorted to a macro. You're right the return is a habit – mattdlockyer Jun 01 '19 at 13:20
2

The nice common thing between Vec, 'Slice' and Array is Iter, so you can zip and map both together, as simple as:

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

This is as the array is 1 dimensional array.

To test all together, vec, slice and array, here you go:

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

Another option which should be faster than byte-by-byte copy is:

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

Which is applicable for all, below is example:

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

Hasan A Yousef
  • 22,789
  • 24
  • 132
  • 203
0

Worth noting that if your array is a string, e.g. if you want to convert &'static str -> [u32; N], then you can sometimes just initialize your original string as b"my string" instead. The b"" syntax in Rust creates statically-known-size arrays of bytes (e.g. &[u8, 32]).

VasiliNovikov
  • 9,681
  • 4
  • 44
  • 62