Initializing array with random numbers

#1

When I try to initialize an array with a number of random values I get an error saying that

'the trait bound `rand::distributions::Standard: rand::distributions::Distribution<[i64; 39]>` is not satisfied.

I am using cargo dependencie rand *
and this code for array init:

let array: [i64; 39]  = rand::random(); 

this works for all array sizes below 30 however.

#2

The trait has to be implemented “manually” for each array size so it’s only implemented for arrays of size <= 32 like the standard library’s traits.
I don’t know a one line solution but you could use an ArrayVec.

#3

Thank you for your swift reply leudz.
If I understand this correctly the suggested list type structure in rust is ArrayVec? I’m coming from Java and C so this is all pretty new to me.

#4

In Rust you have Arrays and Vec. They both store elements of a certain type contiguously in memory but they have a few differences.
You can push elements to a Vec but not to an Array, Vec always puts the elements on the stack heap whereas an array can be on the stack or on the heap and a Vec will re-allocate if it needs to.
Also an Array of 2 elements is a different type from an Array of 3 elements whereas Vec’s type is not dictated by it’s size.
ArrayVec is a little of both, it can grow from 0 to it’s capacity but never re-allocate, it can be on the stack and it’s type is linked to it’s size.
I don’t think Java’s list is any of these.

I suggested ArrayVec because I thought you wanted a fixed size array, if you don’t really care you can do:

let mut vec: Vec<i64> = Vec::with_capacity(39);
for _ in 0..vec.capacity() {
    vec.push(rand::random());
});

If you want a fixed size array you can use an ArrayVec:

let mut array_vec: arrayvec::ArrayVec<[i64; 40]> = arrayvec::ArrayVec::new();
for _ in 0..array_vec.capacity() {
    array_vec.push(rand::random());
}
let array: [i64; 40] = array_vec.into_inner().unwrap();

I just noticed ArrayVec doesn’t exist for 39 elements, 40 is the closest.
Or you could use a macro and get an Array of 39 elements. I think you’ll be good with a Vec, let me know.

1 Like
#6

Oops, I was thinking heap and wrote stack.

#7

Array in Rust is like a fixed array in C and Vec is a standard library abstraction implementing a dynamically allocated, resizeable array on the heap correctly and painlessly. So Array in Rust is like int array[10]; in C and Vec is wrapping int* arrPtr = malloc(sizeof(int) * 10); so you don’t have to do this manually if you want to add an 11th element:

// Create an array of 10 elements on the heap.
int* arrPtr = malloc(sizeof(int) * 10);
for (int i = 0; i < 10; ++i)
    *(arrPtr + sizeof(int) * i) = i;

// Request a new block of heap memory large enough to add the new element
// and copy each element from the old array into the new array
int* newArrPtr = malloc(sizeof(int) * 20);
for (int i = 0; i < 10; ++i)
    *(newArrPtr + sizeof(int) * i) = *(arrPtr + sizeof(int) * i);

// Free the original, small array
free(arrPtr);

// Add the 11th element
*(newArrPtr + sizeof(int) * 10) = 11;

EDIT: 10 is an arbitrary number. It could’ve been any positive int.
EDIT2: Forgot to free momory. Sorry! I am a C++ programmer, where I use std::vector to take care of such shenanigans for me. :see_no_evil:

#8

Thank you all for making this a bi clearer for me. However I a still a bit puzzled as to why there seems to be a limit on the size of array if I initialize it with arbitrary numbers? Is it due to prevent undefined behavior?

#9

Could you explain a bit what do you mean? Array literal, AFAIK, can be of arbitrary size.

#10

Yes, sorry if I have been unclear.
When I init an array with

```
let array: [i64; 30]  = rand::random();
```

It works for array sizes below 30ish, but not over. If I set array size to a larger value, I get the error I posted in my initial post. I got an answer saying it works for array sizes <= 32, however it is a bit unclear to me why this is the case.

#11

The problem is that every array size effectively creates the new type - i.e., [i64; 1] and [i64; 2] are fully independent types. So, if we want to get this types as output, we must have function implementation rand() -> ArrType for every such ArrType. Since we can’t create it for all such types at once (at least while we haven’t got const generics), we must create rand() -> [T, 1], rand() -> [T, 2], and so on (this is made by implementing a trait on output type, in fact). Of course, there will be only some finite amount of these implementations, i.e. finite amount of array sizes which can be used as output.

#12

I see.
Thanks a lot for clearing this up for me.

#13
let mut array = [0_i64; 39];
rand::thread_rng().fill(&mut array);

https://docs.rs/rand/0.6.5/rand/trait.Rng.html#method.fill

5 Likes
#14

But it’s not implemented for [T; 39]

#15

In that case, it’s not acting on a &mut [T; 39], instead it’s on a slice (&mut [T]), so it will work for any of the above containers. &mut array is equivalent to &mut array[..] in this case

#16

You actually need to write rand::thread_rng().fill(&mut array[..]); explicitly. The compiler won’t perform an unsized coercion to satisfy a trait bound on a generic type.

1 Like
#17

Ooh thanks I haven’t noticed it! I usually handle [u8] and .fill_buf() which just work with code like above.

#18

Yeah, fill_bytes() (as I believe it is called) accepts a &mut [u8], and the compiler will perform an implicit unsized coercion from &mut [u8; _] to that type. fill() on the other hand is a generic function and requires some trait implementation on the type. The compiler will infer the generic type from the argument you pass, and then check the trait bounds, but it won’t try random coercions in the hope that one of them leads to a type that implements the trait.

You could also explicitly specify the type, which would result in an implicit unsized coercion again, so this works as well:

rand::thread_rng().fill::<[_]>(&mut array);

I’d prefer the former solution, though, since I find it easier to understand.