3

I have the following code:

const N: usize = 10000;
const S: usize = 7000;

#[derive(Copy, Clone, Debug)]
struct T {
    a: f64,
    b: f64,
    f: f64
}

fn main() {
    let mut t: [T; N] = [T {a: 0.0, b: 0.0, f: 0.0}; N];

    for i in 0..N {
        t[i].a = 0.0;
        t[i].b = 1.0;
        t[i].f = i as f64 * 0.25;
    }

    for _ in 0..S {
        for i in 0..N {
            t[i].a += t[i].b * t[i].f;
            t[i].b -= t[i].a * t[i].f;
        }
        println!("{}", t[1].a);
    }
}

I'm unsure why the array t must be initialized that way. The first for-loop is intended to initialize the array with the containing struct to their respective values.

When I try to omit the initialization directly with the array:

let mut t: [T; N];

I get the following error:

error[E0381]: use of possibly uninitialized variable: t

All for-loops are intended to be as such, I just want to know if there is a smarter way for the array and it's initialization with the first for-loop.

2 Answers 2

7

I'm unsure why the array t must be initialized that way.

Because Rust doesn't let you touch (entirely or partially) uninitialised values. The compiler isn't smart enough to prove that the loop will definitely initialise everything, so it just forbids it.

Now, the optimiser is a different story. That can notice that the initialisation is redundant and skip it... in theory. It doesn't appear to do so with that code and the current compiler. Such is optimisation.

I just want to know if there is a smarter way for the array and it's initialization with the first for-loop.

The smart way is to just leave the code as-it-is. Statistically speaking, it's unlikely to be a bottleneck. If profiling suggests that it is a bottleneck, then you can use uninitialised. However, note that doing so can lead to undefined behaviour if you use it wrong. Although not an exhaustive list, you definitely avoid using it on any type that is not Copy.

If you do need to use it, I strongly recommend also adjusting the first loop to make forgetting to initialise an element or a field in the structure impossible:

    let mut t: [T; N] = unsafe { ::std::mem::uninitialized() };

    for (i, e) in t.iter_mut().enumerate() {
        *e = T {
            a: 0.0,
            b: 1.0,
            f: i as f64 * 0.25,
        }
    }
Sign up to request clarification or add additional context in comments.

2 Comments

If you use std::ptr::write(e, t) rather than *e = t does it eliminate the Copy-only restriction?
@ildjarn: That's got nothing to do with it. I said that because non-Copy types can have custom Drop logic, which can cause Bad Things™ to happen if you make a mistake and drop uninitialised memory. The sort of people who don't need to follow that advice are the sort of people who've already read the Rustonomicon, and aren't likely to need advice from the likes of me.
2

You can use std::mem::uninitialized(). Note, however, that it is considered unsafe and needs to be marked as such:

let mut t: [T; N] = unsafe { std::mem::uninitialized() };

As stated by the aforelinked docs:

This is useful for FFI functions and initializing arrays sometimes, but should generally be avoided.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.