Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GenericArray should implement Index{,Mut} as well as a way to get a pointer to a field #75

Open
Ekleog opened this issue Apr 16, 2019 · 5 comments

Comments

@Ekleog
Copy link

Ekleog commented Apr 16, 2019

This would make it possible to handle partially-uninitialized GenericArrays without insta-UB.

Thank you for your work on generic-array :)

@novacrazy
Copy link
Collaborator

A partially-uninitialized GenericArray at almost any time is asking for undefined behavior. Although it would be fine for Copy types with no Drop, anything else is a bad idea.

However, there are intentionally undocumented but publicly exported structures in generic-array for safely constructing GenericArrays one element at a time.

See here for ArrayBuilder and and example of how it's used.

Likewise, there is also ArrayConsumer for the inverse.

The primary difference is their Drop behavior.

If you must use uninitialized memory with GenericArray, use the above helpers instead. Otherwise, fill them with a default value like zero or use GenericArray::generate/GenericArray::default

As for Index/IndexMut, I can do that. yourarray[0] already works thanks to Deref-ing into a slice, but I can see how implementing the trait itself would be necessary for generics.

@Ekleog
Copy link
Author

Ekleog commented Apr 17, 2019

I'm pretty deep into unsafe stuff, and so long as GenericArray<T, N> behaves like [T; N], I think I'm handling initializing, dropping etc. correctly :) However, as soon as I Deref into a slice I'm getting into the “officially UB but actually works” land, as it's creating a reference to uninitialized data… which I think is an error made here:

pub unsafe fn iter_position(&mut self) -> (slice::IterMut<T>, &mut usize) {

Having Index{,Mut} would hopefully allow not going through a slice and thus safely indexing into a partially-uninitialized GenericArray (assuming it has indeed been initialized at the accessed index).

And the “way to get a pointer to a field” from the title would allow for a way to initialize an element without insta-UB, as getting a reference to a not-yet-initialized item is officially UB.

@novacrazy
Copy link
Collaborator

Dereferencing into a slice is totally fine so long as you don't read uninitialized data or overwrite initialized data. A slice doesn't own the memory, so it can't drop it.

The entire reason ArrayBuilder and ArrayConsumer exist is to handle what happens if something panics during initialization/deconstruction. The Drop implementation on each will drop any created or remaining elements, and leave uninitialized elements as they are. The builder and consumer wrap the array in ManuallyDrop so undefined behavior cannot be triggered.

Slices are a magical construct in Rust, and allow for incredible optimizations when used. In fact, ArrayBuider and ArrayConsumer are typically entirely optimized away for arrays of primitive types, but provide a safety net for arrays of types with Drop implementations.

Something you cannot do is random-access initialize/deinitialize elements, which is what it sounds like you want. If the types you want are Copy primitives without Drop, I'd highly recommend using GenericArray::generate/GenericArray::default first and then accessing them in whatever order you please.

@Ekleog
Copy link
Author

Ekleog commented Apr 17, 2019

Dereferencing into a slice is totally fine so long as you don't read uninitialized data or overwrite initialized data. A slice doesn't own the memory, so it can't drop it.

It is not. The fact the slice won't drop the memory does not make it not-UB to have a slice pointing to undefined memory. rust-lang/rfcs#1892 (comment) explicitly reminds it. And rust-lang/rfcs#1892 (comment) is a nice example of why it's hard to do it any other way, for optimization purposes.

Now, we totally agree that in practice it should work most of the time, but it is not guaranteed by current Rust.

If the types you want are Copy primitives without Drop

Well, my types are not Copy and are Drop, so I've got quite a bit of code that's (trying to) do this correctly :)

My main issue has been that for this I needed to do .as_slice(), even if it was just to access a pointer to the element and ptr::write or ptr::drop_in_place it. Which, while in practice it works, is UB, as I'm having a &[T] for half a tick (ie. just before it's converted to a pointer) in which some items are potentially not actually well-formed Ts.

@korken89
Copy link

Hi, I'd like to revive this issue.
We need something similar for use in creating generic DMA buffers in Rust Embedded.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants