Skip to content

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

Open

Description

@Ekleog

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

Thank you for your work on generic-array :)

Activity

  1. novacrazy commented on Apr 17, 2019

    @novacrazy
    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.

  2. Ekleog commented on Apr 17, 2019

    @Ekleog
    Author

    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.

  3. novacrazy commented on Apr 17, 2019

    @novacrazy
    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.

  4. Ekleog commented on Apr 17, 2019

    @Ekleog
    Author

    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.

  5. korken89 commented on Oct 28, 2019

    @korken89

    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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions