Как получить срез в виде массива в Rust?

У меня есть массив неизвестного размера, и я хотел бы получить фрагмент этого массива и преобразовать его в массив статического размера:

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

Как мне это сделать?

Ответы

Ответ 1

Вы можете легко сделать это с чертой TryInto (которая была стабилизирована в Rust 1.34):

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

Но даже лучше: нет необходимости клонировать/копировать ваши элементы! На самом деле можно получить &[u8; 3] из &[u8]:

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

Как уже упоминалось в других ответах, вы, вероятно, не хотите паниковать, если длина barry не равна 3, а вместо этого корректно обработать эту ошибку.

Это работает благодаря этим ссылкам (где $N - просто целое число от 1 до 32) связанной черты TryFrom:

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

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

Ответ 2

Благодаря @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! если целевой массив и переданный срез не имеют одинаковую длину.

Ответ 3

Здесь функция, соответствующая запрошенной тиковой подписке.

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

Ответ 4

Я рекомендую использовать ящик arrayref, у которого есть удобный макрос для этого.

Обратите внимание, что с помощью этого ящика вы создаете ссылку на массив &[u8; 3], потому что он не клонирует никаких данных!

Если вы хотите клонировать данные, вы все равно можете использовать макрос, но вызывать клон в конце:

#[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()
}

Ответ 5

Вы можете вручную создать массив и вернуть его.

Вот функция, которая может легко масштабироваться, если вы хотите получить больше (или меньше) трех элементов.

Обратите внимание, что если срез слишком мал, конечные члены массива будут равны 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
}

Ответ 6

Я был недоволен другими ответами, потому что мне нужно было несколько функций, которые возвращали бы фиксированные массивы 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>

Ответ 7

Хорошая общая черта между Vec, 'Slice' и Array - это Iter, так что вы можете zip и map оба вместе, просто:

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, slice и array, здесь вы идете:

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

Ответ 8

Вы можете создать собственный макрос для решения проблемы. Ограничение здесь, вы должны указать размер среза как константу. Вы не можете создать массив с динамической длиной, который объясняется по следующим вопросам:

Как установить длину массива Rust динамически?

Возможно ли иметь выделенные в стек массивы с размером, определенным во время выполнения в 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);
}

Для рабочего кода: детская площадка