How to perform efficient vector initialization in Rust?
Asked Answered
J

1

5

What's a good way to fill in a vector of structs in Rust where:

  • The size is dynamic, but known at the time of initialization.
  • Doesn't first initialize the memory to a dummy value.
  • Doesn't re-allocate memory as its filled.
  • In this example, all members of the vector are always initialized.
    (In keeping with Rusts assurance of no undefined behavior).

And ideally

  • Doesn't index check each index access
    (since the size is known when declaring the vector this should be possible).
  • Doesn't require unsafe
    (Not sure if this is reasonable, however the compiler _could_ detect that all values are always filled, allowing such logic in an unsafe block).

The C equivalent is:

struct MyStruct *create_mystruct(const uint n) {
    struct MyStruct *vector = malloc(sizeof(*vector) * n);
    for (uint i = 0; i < n; i++) {
        /* any kind of initialization */
        initialize_mystruct(&vector[i], i);
    }
    return vector;
}

I'm porting over some C code which fills an array in a simple loop, so I was wondering if there was a Rustic way to perform such a common task with zero or at least minimal overhead?

If there are typically some extra checks needed for the Rust version of this code, what's the nearest equivalent?

Jokjakarta answered 10/8, 2016 at 2:27 Comment(2)
P
11

Just use map and collect.

struct MyStruct(usize);

fn create_mystructs(n: usize) -> Vec<MyStruct> {
    (0..n).map(MyStruct).collect()
}

"Initializing" doesn't make sense in safe Rust because you'd need to have the ability to access the uninitialized values, which is unsafe. The Iterator::size_hint method can be used when collecting into a container to ensure that a minimum number of allocations is made.

Basically, I'd trust that the optimizer will do the right thing here. If it doesn't, I'd believe that it eventually will.

Profitsharing answered 10/8, 2016 at 2:48 Comment(3)
How would the initialize_mystruct fit in here? Also, I assume Rust is smart enough to detect that the iterator has a fixed size and not resize the vector while running collect?Jokjakarta
@Jokjakarta You'd use MyStruct's constructor instead of initialize_mystruct, and then do .map(MyStruct::new). Rust knows that map preserves the length and will allocate properly, yes.Eyeball
And since I used a tuple struct, MyStruct's constructor is just MyStruct, the function called in the map.Profitsharing

© 2022 - 2024 — McMap. All rights reserved.