1

i want to make a 2D fixed size array that is going to be initialized in a for loop. This is the sample code (The Cell struct can not implement Copy or Default trait for some reason):

let mut cells: [[Cell; 20]; 20];
for i in 0..20 {
    for j in 0..20 {
        cells[i][j] = Cell::new(some_value_based_on_i_and_j);
    }
}

but then compiler gives this error:

use of possibly-uninitialized variable

So in order to assure the compiler that my array is fully initialized for at least 1 time; i change it to something like this:

let mut cells: [[Cell; 20]; 20] = [[Cell::new(default_value); 20]; 20];
for i in 0..20 {
    for j in 0..20 {
        cells[i][j] = Cell::new(some_value_based_on_i_and_j);
    }
}

(in fact i want to listen to the compiler and initialize all of the indices of my array with a default value like this pattern and then do my previous job):

 let a = [-12, 100] // means -> [default_value, repetition_or_also_length_of_the_array]

Then the compiler gives this error:

the trait bound [maze_maker::Cell; 20]: std::marker::Copy is not satisfied

the trait std::marker::Copy is not implemented for [maze_maker::Cell; 20]

note: the Copy trait is required because the repeated element will be copied

What should i do ): ?

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
  • does that answer your question: https://stackoverflow.com/questions/67703525/initialize-array-of-arrays-from-iterator , https://doc.rust-lang.org/std/mem/union.MaybeUninit.html ? – Adam Jun 03 '21 at 13:14
  • Thank you very much but it was a little complex and some part of it also used unsafe code – Amirhosein_GPR Jun 03 '21 at 13:17
  • To initialize an array iteratively the common way is to use Maybeuninit which is inherently unsafe. If you guarantee the result is fully initialized, it's perfectly safe (although it contains an unsafe block) – Adam Jun 03 '21 at 13:21
  • 1
    So this can be my real answer! thank you mate. I do appreciate you – Amirhosein_GPR Jun 03 '21 at 13:25
  • Alternatively, depending on your performance constraints (or the lack thereof) you can create a `Vec` (or multiple vecs), then try and [convert to a fixed size array](https://doc.rust-lang.org/std/primitive.array.html#impl-TryFrom%3CVec%3CT%2C%20A%3E%3E). Using a 1-dimension storage backend would probably be simpler though, converting the inner vecs then the outer in two passes would be rather complex and somewhat expensive allocations-wise. – Masklinn Jun 03 '21 at 13:56
  • Yes you're right. That's a good idea! maybe i use your method. thank you very much :D – Amirhosein_GPR Jun 03 '21 at 14:06

2 Answers2

1

The array_init crate may help you here, it lets you create an array either by giving it a function it will call repeatedly, or by collecting an iterator.

Daniel Wagner-Hall
  • 2,446
  • 1
  • 20
  • 18
1

If you don't mind temporary allocation, you can make use of the fact that Vec<T> implements TryInto<[T; N]>, returning a run-time error if the length of the Vec doesn't fit the size of the array. It's not particularly elegant, but requires no unsafe:

let cells: [[Cell<usize>; 20]; 20] = (0..20)
    .map(|i| {
        (0..20)
            .map(|j| Cell::new(i + j))
            .collect::<Vec<_>>()
            .try_into()
            .unwrap()
    })
    .collect::<Vec<_>>()
    .try_into()
    .unwrap();

If you're ok with external crates, you can adapt the above to use arrayvec to avoid allocation:

let cells: [[Cell<usize>; 20]; 20] = (0..20)
    .map(|i| {
        (0..20)
            .map(|j| Cell::new(i + j))
            .collect::<ArrayVec<_, 20>>()
            .into_inner()
            .unwrap()
    })
    .collect::<ArrayVec<_, 20>>()
    .into_inner()
    .unwrap();

On the other hand, if you're already using external crates, then the array_init crate recommended by the other answer might be a better fit.

user4815162342
  • 141,790
  • 18
  • 296
  • 355