¿Cómo puedo crear una matriz de cadenas de tamaño fijo utilizando genéricos constantes?

Aug 17 2020

Tengo una función que usa un genérico constante:

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
}

¿Cómo puedo crear una matriz de tamaño fijo de Strings en mi caso? let mut row: [String; S] = ["".to_string(), S];no funciona porque Stringno implementa el Copyrasgo.

Respuestas

4 eggyal Aug 17 2020 at 23:49

Puedes hacerlo con MaybeUninity 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!()
}

Por supuesto, podría ser más fácil abstraer esa lógica en tu propio rasgo:

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 única razón para usar su propio rasgo en lugar de Defaultes que las implementaciones de este último entrarían en conflicto con las proporcionadas en la biblioteca estándar para matrices de hasta 32 elementos; espero que la biblioteca estándar reemplace su implementación Defaultcon algo similar a la arriba una vez que los genéricos const se hayan estabilizado).

En cuyo caso ahora tendría:

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

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

    // Some code

    todo!()
}

Véalo en el patio de juegos .