GolDDranks
GolDDranks

Reputation: 3562

Convincing the compiler that every index of an array will be initialized

I'm trying to implement the Rand trait for a buffer of 20 elements that implement the Rand trait themselves. I iterate the buffer through and initialize every element with a random value. However, I'm unable to convince the compiler that buff will end up fully initialized.

What should I do to get it to accept this?

extern crate rand;
use rand::{Rand, Rng};

struct Buf20<T>([T; 20]);

impl<T: Rand> Rand for Buf20<T> {
    fn rand<R: Rng>(rng: &mut R) -> Self {
        let mut buff : [T; 20];
        for element in &mut buff {
            *element = rng.gen();
        }
        Buf20(buff)
    }
}

Upvotes: 2

Views: 188

Answers (1)

Chris Morgan
Chris Morgan

Reputation: 90902

You won’t be able to convince the compiler of this, because it’s not necessarily true.

There are two unknown types here, T and R, and their interactions could break things here. Here’s a couple of possible implementations of Rand and Rng that would make it blow up:

struct T(Box<u32>);

impl Rand for T {
    fn rand<R: Rng>(rng: &mut R) -> Self {
        T(Box::new(rng.next_u32()))
    }
}

struct R(u32);

impl Rng for R {
    fn next_u32(&mut self) -> u32 {
        let next = self.0;
        self.0 += 1;
        if self.0 > 10 {
            panic!();
        }
        next
    }
}

Basically, if it is possible for R to trigger a panic and the T has a destructor, you’re entering undefined behaviour, because the elements of the array may not all not be initialised at that stage. Box<T> is a particularly good example of such undefined behaviour, because you’ll be trying to free an undefined memory address.

This is why such usage of an array would need to either (a) have a length member to show how many of the items in the array have data so that you can skip destructors on the rest (this would require more unsafe code, of course, and simply disabling destructors is not something that is supported natively at present), or else (b) wrap each item with Option, setting the uninitialised values to None so that the illegal value’s destructor won’t be run.

Or (c) you could use vectors.

Fixed-size arrays are very much second class citizens in Rust at present.

Upvotes: 5

Related Questions