web-dev-qa-db-fra.com

Comment obtenir une tranche sous forme de tableau à Rust?

J'ai un tableau de taille inconnue, et j'aimerais obtenir une tranche de ce tableau et le convertir en tableau de taille statique:

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

Comment je ferais ça? 

25
Jeroen Bollen

Voici une fonction qui correspond à la signature de type que vous avez demandée.

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

Mais comme barry peut avoir moins de trois éléments, vous voudrez peut-être renvoyer un Option<[u8; 3]> plutôt qu'un [u8; 3].

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

Merci à @malbarbo nous pouvons utiliser cette fonction d'assistance:

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
}

pour obtenir une syntaxe beaucoup plus propre:

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

aussi longtemps que T: Default + Clone.

Si vous savez que votre type implémente Copy, vous pouvez utiliser ce formulaire:

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
}

Les deux variantes panic! si le tableau cible et la tranche transmise n’ont pas la même longueur.

16
Matthieu M.

Je recommande d'utiliser crate arrayref , qui dispose d'une macro pratique pour le faire.

Notez qu'en utilisant cette caisse, vous créez une référence à un tableau, &[u8; 3], car il ne clone aucune donnée!

Si vous do voulez cloner les données, vous pouvez toujours utiliser la macro, mais appelez clone à la fin:

#[macro_use]
extern crate arrayref;

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

ou

#[macro_use]
extern crate arrayref;

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

Vous pouvez créer manuellement le tableau et le renvoyer.

Voici une fonction qui peut facilement évoluer si vous voulez obtenir plus de (ou moins) que 3 éléments.

Notez que si la tranche est trop petite, les termes de fin du tableau seront 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
}
10
Levans

Cette réponse utilise la fonctionnalité instable try_from!


Vous pouvez facilement le faire avec le tout nouveau TryInto trait (qui est toujours instable en juin 2018):

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

Mais encore mieux: il n’est pas nécessaire de cloner votre tableau! Il est effectivement possible d’obtenir un &[u8; 3] à partir d’un &[u8]:

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

Comme mentionné dans les autres réponses, vous ne voudrez probablement pas paniquer si la longueur de barry n'est pas 3, vous devriez donc renvoyer un Option<[u8; 3]> ou quelque chose de similaire pour gérer cette erreur avec élégance.

Cela fonctionne grâce à ces impls (où $N est juste un entier compris entre 1 et 32) du trait associé TryFrom :

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

Vous pouvez créer une macro personnalisée pour résoudre le problème . La contrainte ici, vous devez spécifier une taille de tranche constante. Vous ne pouvez pas créer de tableau de longueur dynamique, ce qui est expliqué à ces questions:

Comment définir dynamiquement une longueur de tableau Rust?

Est-il possible d'avoir des tableaux de pile allouée avec la taille déterminée au moment de l'exécution dans 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);
}

Pour code de travail: terrain de jeu

0
Akiner Alkan