Comment puis-je créer un tableau de taille fixe de chaînes à l'aide de génériques constants?

Aug 17 2020

J'ai une fonction utilisant une constante générique:

fn foo<const S: usize>() -> Vec<[String; S]> {
    // Some code
    let mut row: [String; S] = Default::default(); //It sucks because of default arrays are specified up to 32 only
    // Some code
}

Comment puis-je créer un tableau de taille fixe de Strings dans mon cas? let mut row: [String; S] = ["".to_string(), S];ne fonctionne pas car Stringne met pas en œuvre le Copytrait.

Réponses

4 eggyal Aug 17 2020 at 23:49

Vous pouvez le faire avec MaybeUninitet unsafe:

use std::mem::MaybeUninit;

fn foo<const S: usize>() -> Vec<[String; S]> {
    // Some code

    let mut row: [String; S] = unsafe {
        let mut result = MaybeUninit::uninit();
        let start = result.as_mut_ptr() as *mut String;
        
        for pos in 0 .. S {
            // SAFETY: safe because loop ensures `start.add(pos)`
            //         is always on an array element, of type String
            start.add(pos).write(String::new());
        }

        // SAFETY: safe because loop ensures entire array
        //         has been manually initialised
        result.assume_init()
    };

    // Some code

    todo!()
}

Bien sûr, il pourrait être plus facile d'abstraire une telle logique à votre propre trait:

use std::mem::MaybeUninit;

trait DefaultArray {
    fn default_array() -> Self;
}

impl<T: Default, const S: usize> DefaultArray for [T; S] {
    fn default_array() -> Self {
        let mut result = MaybeUninit::uninit();
        let start = result.as_mut_ptr() as *mut T;
        
        unsafe {
            for pos in 0 .. S {
                // SAFETY: safe because loop ensures `start.add(pos)`
                //         is always on an array element, of type T
                start.add(pos).write(T::default());
            }

            // SAFETY: safe because loop ensures entire array
            //         has been manually initialised
            result.assume_init()
        }
    }
}

(La seule raison d'utiliser votre propre trait plutôt que Defaultc'est que les implémentations de ce dernier entreraient en conflit avec celles fournies dans la bibliothèque standard pour des tableaux de 32 éléments maximum; je m'attends totalement à ce que la bibliothèque standard remplace son implémentation de Defaultpar quelque chose de similaire au ci-dessus une fois que les génériques const se sont stabilisés).

Dans ce cas, vous auriez maintenant:

fn foo<const S: usize>() -> Vec<[String; S]> {
    // Some code

    let mut row: [String; S] = DefaultArray::default_array();

    // Some code

    todo!()
}

Voyez-le sur le Playground .