How can I initialize an array using a function? [duplicate]
Asked Answered
S

5

17

I want to create an array of 10 empty vectors in Rust, but [Vec::new(); 10] doesn't work as Vec doesn't implement Copy. How can I do this, and in more general terms how can I initialize a array by repeatedly calling a function?

Shit answered 28/4, 2016 at 21:22 Comment(3)
Can you show us what you've tried? There should be no issue returning an array out of a function.. or a vector. Which one have you tried and how? (your title says array, your question says vector).Bratwurst
Whoops, you're right - that's a typoShit
Related: Why is the Copy trait needed for default (struct valued) array initialization? and What is the proper way to initialize a fixed length array?. Duplicate of Initialize a large, fixed-size array with non-Copy types.Meeting
S
13

As of Rust 1.63

Use from_fn:

let array: [usize; 5] = core::array::from_fn(|i| i * 2);
assert_eq!(array, [0, 2, 4, 8, 10]);
Smokestack answered 15/8, 2022 at 8:21 Comment(0)
U
10

As of Rust 1.55.0, Sep 2021

You can use array's .map() method for general functions:

let arr: [Vec<u32>; 10] = [(); 10].map(|_| Vec::with_capacity(100));

(With Rust 1.63.0, Aug 2022 consider using from_fn).

Unprepared answered 7/10, 2021 at 21:25 Comment(0)
M
5

For your specific case, you can just use Default:

let v: [Vec<String>; 10] = Default::default();

For the general case, you can create an iterator out of your function and then collect into the array using ArrayVec:

use arrayvec::ArrayVec; // 0.4.10
use std::iter;

fn make<R>(f: impl FnMut() -> R) -> [R; 10] {
    let a: ArrayVec<_> = iter::repeat_with(f).collect();
    a.into_inner()
        .unwrap_or_else(|_| panic!("Did not have enough elements"))
}

fn main() {
    let mut a = 0;
    let arr = make(|| {
        a += 3;
        a
    });

    println!("{:?}", arr);
    // [3, 6, 9, 12, 15, 18, 21, 24, 27, 30]
}

See also:

Meeting answered 20/3, 2018 at 15:59 Comment(0)
S
3

I see two possible approaches

First

A simple solution using macro

macro_rules! array {
    ($v: expr; 1) => ([$v]);
    ($v: expr; 2) => ([$v, $v]);
    ($v: expr; 3) => ([$v, $v, $v]);
    ($v: expr; 4) => ([$v, $v, $v, $v]);
    ($v: expr; 5) => ([$v, $v, $v, $v, $v]);
    // until 32
}

let a = array![Vec::new(); 3];

It's a bit verbose, but even the standard library uses this kind of construct.

Second

After realizing a connection between this question and another that I had answered before, I wrote this solution using nodrop

extern crate nodrop;

macro_rules! array {
    ($e: expr; $n:expr) => (
        {
            use std::mem;
            use std::ptr;
            use nodrop::NoDrop;

            struct ArrayBuilder<T> {
                len: usize,
                data: *mut T,
            }

            impl<T> Drop for ArrayBuilder<T> {
                fn drop(&mut self) {
                    unsafe {
                        while self.len > 0 {
                            let offset = (self.len as isize) - 1;
                            self.len -= 1;
                            ptr::drop_in_place(self.data.offset(offset));
                        }
                    }
                }
            }

            let mut v: NoDrop<[_; $n]> = NoDrop::new(unsafe {
                mem::uninitialized()
            });
            // helps type inference for v
            if false { v[0] = $e; }
            let mut builder = ArrayBuilder {
                len: 0,
                data: (&mut *v as *mut _) as *mut _
            };
            unsafe {
                for i in 0..$n {
                    ptr::write(builder.data.offset(i as isize), $e);
                    builder.len = i + 1;
                }
            }
            builder.len = 0;
            v.into_inner()
        }
    )
}

let a = array![Vec::new(); 3];

And a test that indicates that it does not leak memory

#[test]
fn test() {
    static mut COUNT: usize = 0;

    #[derive(Debug)]
    struct X(usize);

    impl Drop for X {
        fn drop(&mut self) {
            println!("drop {:?}", self.0);
        }
    }

    impl X {
        fn new() -> X {
            unsafe {
                if COUNT == 3 {
                    panic!();
                }
                let x = X(COUNT);
                COUNT += 1;
                x
            }
        }
    }

    array![X::new(); 6];
}

In this test, the method X::new panics when creating X(3), so X(0), X(1), X(2) must be dropped.

Others

There is an unsafe solution here.

Subshrub answered 28/4, 2016 at 22:42 Comment(2)
Would it be theoretically possible to create a macro which can generate a construct like this for any n? My own attempts have been stymied by the rule of rust macros expanding to a single expression, making it hard to have a variable number of expressions inside the square brackets.Shit
With the current macro system, it's not possible.Subshrub
K
1

Edit: Outdated (applies to a Rust version lower than 1.55.0):

There is a crate called arr_macro that does exactly what you want:

fn main() {
    let array: [Vec<String>; 10] = arr![Vec::new(); 10];
    println!("{:?}", array) // [[], [], [], [], [], [], [], [], [], []]
}
Karlmarxstadt answered 21/7, 2020 at 19:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.