How can I initialize an array using a function?

后端 未结 3 2128
挽巷
挽巷 2020-11-28 16:24

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

相关标签:
3条回答
  • 2020-11-28 16:58

    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:

    • How do I collect into an array?
    0 讨论(0)
  • 2020-11-28 17:19

    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) // [[], [], [], [], [], [], [], [], [], []]
    }
    
    0 讨论(0)
  • 2020-11-28 17:22

    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.

    0 讨论(0)
提交回复
热议问题