2

Sometimes its useful to create new values for a fixed size array in a loop:

fn foo(u: f64) -> [f64; 3] {
    let mut ret = [-1.0; 3];  // -1 is never used!
    for i in 0..3 {
        ret[i] = some_calculation(u, i);
    }
    return ret;
}

While this works, it's a bit weak to create an array filled with a value which is never used.

An alternative is to manually unroll, but this isn't so nice for larger fixed sized arrays or when the expression is more involved then the example given:

fn foo(u: f64) -> [f64; 3] {
    return [
        some_calculation(u, 0),
        some_calculation(u, 1),
        some_calculation(u, 2),
    ];
}

Does Rust provide a way to do something roughly equivalent Python's list comprehension?

fn foo(u: f64) -> [f64; 3] {
    return [some_calculation(u, i) for i in 0..3];
}

I am a beginner who has very little experience with iterators.

1
  • 1
    ret should be mutable and you don't need to return it explicitly; just "ret" in the last line works just fine. Commented Sep 5, 2016 at 6:23

3 Answers 3

4

Rust guarantees memory safety in its default mode (outsides unsafe blocks).

In order to do so, it must guarantee that no uninitialized memory is ever accessed, which translates (for arrays) in guaranteeing that they are fully initialized no matter what happens.

A clever analysis could check that your loop will fully initialize it, but would probably not be able to prove it works in more complicated cases, so the experience would be inconsistent, and jarring when a simple change in the function would suddenly cause you to have to come back to the array and fully initialize it now that the compiler can no longer prove it works.

So, instead, Rust took the following approach:

  1. Ask the user to fully initialise the array (by providing a copyable value)
  2. Rely on the optimizer to eliminate redundant writes

In case the second step fails in a particular setup, a user can use unsafe { std::men::uninitialized() } to tell the compiler that it takes it upon itself to guarantee it is fully initialized.

This approach is always safe, often as fast, ... and incredibly annoying when you are unfortunate enough not to be working with a Copy type. In this latter case, a simple strategy is to first build a Vec, and then move its elements into an array with a simple for loop, hopefully the optimizer should elide all the unnecessary stuff afterward.

Sign up to request clarification or add additional context in comments.

4 Comments

Agree unsafe in this case would be a last resort (or only to use in isolated cases when it can be proven to make a tangible difference). However AFAICS unsafe isn't necessarily the only way. It should be possible to use a macro that unrolls an expression into an one that initializes all elements of the array, eg: [unroll_n!(some_expr(i), i, 3)].
@ideasman42: We would need a macro wizard for a definite answer, but I am not sure that it is actually feasible to instruct a macro to repeat a piece of code X number of times. The variable construct (*) expands as many times as the arguments did, which require the caller to pass as many arguments as necessary (workable for 3, but for 42 it's annoying).
@ideasman42: (Note: in the comment above I purposely ignored the idea of using pattern-matching in the macro implementation and special-casing a small number of array sizes, however it would obviously be a possibility)
Attempt at macro (failed): play.rust-lang.org/…
1

If you're okay using an unsafe block, you can do:

fn foo(u: f64) -> [f64; 3] {
    let mut ret : [f64; 3] = unsafe { std::mem::uninitialized() };
    for i in 0..3 {
        ret[i] = some_calculation(u, i);
    }
    return ret;
}

2 Comments

Though it works, I would not call it a best practice in Rust.
Using a direct assignment is only safe for types that don't implement Drop (otherwise, the destructor will be called on an uninitialized value!). In general, ptr::write should be used to assign a value to an uninitialized memory location.
0

Arrays in Rust are always initialized: Rust reference, SO question on converting Vecs to arrays, so this shouldn't be a concern (especially if you are after the best Rust practice).

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.