Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Initialize rest of array with a default value

Tags:

rust

Is there a way in Rust to initialize the first n elements of an array manually, and specify a default value to be used for the rest?

Specifically, when initializing structs, we can specify some fields, and use .. to initialize the remaining fields from another struct, e.g.:

let foo = Foo {
  x: 1,
  y: 2,
  ..Default::default()
};

Is there a similar mechanism for initializing part of an array manually? e.g.

let arr: [i32; 5] = [1, 2, ..3];

to get [1, 2, 3, 3, 3]?

like image 843
Kasra Ferdowsi Avatar asked Oct 27 '22 09:10

Kasra Ferdowsi


People also ask

How do you initialize an entire array with value?

int num[5] = {1, 1, 1, 1, 1}; This will initialize the num array with value 1 at all index. The array will be initialized to 0 in case we provide empty initializer list or just specify 0 in the initializer list. Designated Initializer: This initializer is used when we want to initialize a range with the same value.

How do you initialize a default array?

Using default values in initialization of arrayType[] arr = new Type[capacity]; For example, the following code creates a primitive integer array of size 5 . The array will be auto-initialized with a default value of 0 .

How do you initialize an array in C with default values?

Initialize Arrays in C/C++The array will be initialized to 0 if we provide the empty initializer list or just specify 0 in the initializer list. d. If the calloc() function is used, it will allocate and initialize the array with 0.

How do you create an array with default value in Java?

To initialize an Array with default values in Java, the new keyword is used with the data type of the Array The size of the Array is then placed in the rectangular brackets. int[] myArr = new int[10]; The code line above initializes an Array of Size 10.


2 Answers

Edit: I realized this can be done on stable. For the original answer, see below.

I had to juggle with the compiler so it will be able to infer the type of the array, but it works:

// A workaround on the same method on `MaybeUninit` being unstable.
// Copy-paste from https://doc.rust-lang.org/stable/src/core/mem/maybe_uninit.rs.html#943-953.
pub unsafe fn maybe_uninit_array_assume_init<T, const N: usize>(
    array: [core::mem::MaybeUninit<T>; N],
) -> [T; N] {
    // SAFETY:
    // * The caller guarantees that all elements of the array are initialized
    // * `MaybeUninit<T>` and T are guaranteed to have the same layout
    // * `MaybeUninit` does not drop, so there are no double-frees
    // And thus the conversion is safe
    (&array as *const _ as *const [T; N]).read()
}

macro_rules! array_with_default {
    (@count) => { 0usize };
    (@count $e:expr, $($rest:tt)*) => { 1usize + array_with_default!(@count $($rest)*) };

    [$($e:expr),* ; $default:expr; $default_size:expr] => {{
        // There is no hygiene for items, so we use unique names here.
        #[allow(non_upper_case_globals)]
        const __array_with_default_EXPRS_LEN: usize = array_with_default!(@count $($e,)*);
        #[allow(non_upper_case_globals)]
        const __array_with_default_DEFAULT_SIZE: usize = $default_size;
        let mut result = unsafe { ::core::mem::MaybeUninit::<
            [::core::mem::MaybeUninit<_>; {
                __array_with_default_EXPRS_LEN + __array_with_default_DEFAULT_SIZE
            }],
        >::uninit().assume_init() };

        let mut dest = result.as_mut_ptr();
        $(
            let expr = $e;
            unsafe {
                ::core::ptr::write((*dest).as_mut_ptr(), expr);
                dest = dest.add(1);
            }
        )*
        for default_value in [$default; __array_with_default_DEFAULT_SIZE] {
            unsafe {
                ::core::ptr::write((*dest).as_mut_ptr(), default_value);
                dest = dest.add(1);
            }
        }

        unsafe { maybe_uninit_array_assume_init(result) }
    }};
}

Playground.


Based on the example from @Denys, here is a macro that works on nightly. Note that I had problems matching the .. syntax (though I'm not entirely sure that's impossible; just didn't put much time into that):

#![feature(generic_const_exprs)]
#![allow(incomplete_features)]

use std::mem::MaybeUninit;

pub fn concat_arrays<T, const N: usize, const M: usize>(a: [T; N], b: [T; M]) -> [T; N + M] {
    unsafe {
        let mut result = MaybeUninit::<[T; N + M]>::uninit();
        let dest = result.as_mut_ptr().cast::<[T; N]>();
        dest.write(a);
        let dest = dest.add(1).cast::<[T; M]>();
        dest.write(b);
        result.assume_init()
    }
}

macro_rules! array_with_default {
    [$($e:expr),* ; $default:expr; $default_size:expr] => {
        concat_arrays([$($e),*], [$default; $default_size])
    };
}

fn main() {
    dbg!(array_with_default![1, 2; 3; 7]);
}

Playground.

like image 133
Chayim Friedman Avatar answered Dec 19 '22 13:12

Chayim Friedman


As another option, you can build a default filled array and just modify the positions you require in runtime:

#![feature(explicit_generic_args_with_impl_trait)]

fn array_with_default_and_positions<T: Copy, const SIZE: usize>(
    default: T,
    init_values: impl IntoIterator<Item = (usize, T)>,
) -> [T; SIZE] {
    let mut res = [default; SIZE];
    for (i, e) in init_values.into_iter() {
        res[i] = e;
    }
    res
}

Playground

Notice the use of #![feature(explicit_generic_args_with_impl_trait)],which is nightly, it could be replaced by an slice since T and usize are copy:

fn array_with_default_and_positions_v2<T: Copy, const SIZE: usize>(
    default: T,
    init_values: &[(usize, T)],
) -> [T; SIZE] {
    let mut res = [default; SIZE];
    for &(i, e) in init_values.into_iter() {
        res[i] = e;
    }
    res
}
like image 40
Netwave Avatar answered Dec 19 '22 14:12

Netwave