4

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?

ideasman42
  • 42,413
  • 44
  • 197
  • 320
  • See also http://stackoverflow.com/q/20734743/155423, http://stackoverflow.com/q/27062874/155423, http://stackoverflow.com/q/27393166/155423, http://stackoverflow.com/q/28656387/155423, http://stackoverflow.com/q/29530011/155423, http://stackoverflow.com/q/31360993/155423. – Shepmaster Aug 10 '16 at 02:41
  • Checked all the other questions, don't think they're direct duplicates (though some are close). – ideasman42 Aug 10 '16 at 02:55

1 Answers1

10

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.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • 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? – ideasman42 Aug 10 '16 at 02:50
  • 1
    @ideasman42 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. – Veedrac Aug 10 '16 at 03:19
  • And since I used a *tuple struct*, `MyStruct's` constructor is just `MyStruct`, the function called in the `map`. – Shepmaster Aug 10 '16 at 12:15