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.
[1, 2, ..3]is not equal to the struct update syntax since there you put whole instance after the ellipsis. What it should be is[1, 2, ..[3; 5]]. I don't think a syntax like that exist, however.vecd![1, 2, ; 3; 5]) ?