Does MaybeUninit::assume_init() copy?

I got paranoid that MaybeUninit::assume_init() was making a copy of the data. For small things, who cares. But I'm going to end up with a large thing.

I've been playing with this code.

Showing the assembly in debug there's a wall of movaps and movups instructions between the two marks. (search for the first call to "playground::mark") That looks like the array is being copied.

Unfortunately, in release it looks like the optimizer has essentially flattened the entire program into moving constants into the array then printing the one value. (Hurray optimizer! Except it prevents me from answering the question.)

So, does MaybeUninit::assume_init() copy?

Notionally, yes. It takes self by value and returns T, and as a variable with a different type, it has to go in a different (notional) stack slot too.

It may optimize out, but that isn't guaranteed.

Ultimately you may want something like Box::new_uninit if you need guarantees, but it's not stable yet. But it just did go into FCP, so maybe it'll be stable later this year.

1 Like

I've tried to force it to show the semantics more clearly by sprinkling a couple of inline(never)s and returning the array from function instead of printing - playground. Seems that all the copying is in place, unfortunately.

2 Likes

Alternatively: If you don't want to move it, presumably you're going to construct it and then pass references around instead of passing it by value or returning it. And you're already using unsafe. So why not just use assume_init_mut or friends?

(And if you are moving it, why not just use assume_init in the call or return expression directly?)

6 Likes

Thanks y'all!

Yes. Exactly that.

But, the work is for an open-source crate so I'm hoping for something already on stable.

The goal is for the buffer to start life on the heap. It will be a reusable buffer passed between threads. The initialization will be done with an operating system call. Ideally the transitions are...

  • Allocate a new buffer. It starts uninitialized.
  • Call the operating system. On success the buffer is initialized and ready for processing. Otherwise the buffer is kept for another attempt.
  • Pass ownership of the buffer to another thread.
  • The second thread processes the data.
  • The buffer is set to uninitialized and is returned to the first thread.

The actual data is similar to Fuffing in the example. It can be trivially dropped. But, I can see an advantage to getting this correct so arbitrary data can be exchanged; some "augmentation" data stored by the first thread.

I like that. That certainly seems both workable and a reasonable way to represent the model.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.