arrayvec icon indicating copy to clipboard operation
arrayvec copied to clipboard

A vec! like macro

Open nico-abram opened this issue 5 years ago • 7 comments

Maybe arrvec! or arr_vec! ?

nico-abram avatar Aug 13 '19 01:08 nico-abram

We already have ArrayVec::from([array literal here]) which serves this purpose quite well, not sure what the benefits of a macro are.

bluss avatar Aug 15 '19 11:08 bluss

@bluss The macro allows you to concisely initialize an arrayvec with a potentially larger backing store.

Say I have the following type code:

type StorageType = ArrayVec<[u64; 20]>;

pub fn main() {
    // This fails, due to mismatched types, expected an array with 20 elements, got an array with 1 element.
    let s: StorageType = ArrayVec::from([1]);
}

There's no really good way to initialize from a macro without copying, but a simple way to implement an arrayvec macro would be:

#[macro_export]
macro_rules! arrvec {
    // This only works if the ArrayVec is the same size as the input array.
    ($elem:expr; $n:expr) => ({
        $crate::ArrayVec::from([$elem; $n])
    });
    // This just repeatedly calls `push`. I don't believe there's a concise way to count the number of expressions.
    ($($x:expr),*$(,)*) => ({
        // Allow an unused mut variable, since if the sequence is empty,
        // the vec will never be mutated.
        #[allow(unused_mut)] {
            let mut vec = $crate::ArrayVec::new();
            $(vec.push($x);)*
            vec
        }
    });
}

Please feel free to use that code.

If you'd like to count the number of elements, and then directly initialize and set the length, you could likely do that with:

macro_rules! count {
    () => (0usize);
    ( $x:tt $($xs:tt)* ) => (1usize + count!($($xs)*));
}

However, you'd likely have to use a for loop to assign by index, because I don't believe there's a generic way to count the number of elements you'd have to pad the array with using a macro, which pretty much defeats the purpose.

Alexhuszagh avatar Sep 01 '19 23:09 Alexhuszagh

Interesting. Counting expressions -- see the little book of rust macros, or the maplit macros. But is not needed - you can write it to use extend with an exact size arrayvec into the inferred size one. That's kind of nice.

bluss avatar Sep 02 '19 10:09 bluss

@Alexhuszagh I've thought about this, and

  1. To be efficient, that macro needs a method that implements something like this: ArrayVec::<[_; 128]>::from_array([1, 2, 3, 4]) which allows starting with a shorter array than the full capacity. And that's easy and efficient to implement. We avoid push, which would be slow. Main challenge I see here is to give it a name, because just from is already taken :slightly_smiling_face:

  2. Then we have a method that serves the purpose and don't need a macro. Macros are only necessary when we can't express it with regular methods.

bluss avatar Sep 29 '19 18:09 bluss

You can use this: https://crates.io/crates/array-macro E.g.:

ArrayVec::from(array![vec![]; 16])

But it may not be in-place (may move the whole array)..

Boscop avatar Oct 05 '19 01:10 Boscop

I think such a macro would be nice, even if the only advantage was that it made arrayvec work more like Vec and SmallVec. This would make it easier to remember how to construct an "ArrayVec literal".

avl avatar Jul 11 '20 08:07 avl

@Alexhuszagh What about ArrayVec::from_iter([x])? Wouldn't it result in very optimized code?

Boscop avatar Jul 06 '23 14:07 Boscop