orlp
orlp

Reputation: 117641

Converting array of mutable pointers to mutable references with const generics

Suppose that I guarantee (through unspecified means) that I have an array of type [*mut T; N] containing N valid and disjoint mutable pointers, all with lifetime 'a. How can I convert this to an array of mutable references [&'a mut T; N]?

In other words, how can I implement this function?

unsafe fn ptrs_to_refs<'a, T, const N: usize>(ptrs: [*mut T; N]) -> [&'a mut T; N] {
    // What goes here?
}

I'm not particularly looking for an implementation that uses an allocation step (e.g. using Vec).


Because min_const_generics is set to stabilize in Rust 1.51, this question is targetting a hypothetical stable Rust that also has min_const_generics. I am looking for answers compatible with these requirements, using no other unstable features.

E.g. with the feature array_map one can simply

unsafe fn ptrs_to_refs<'a, T, const N: usize>(ptrs: [*mut T; N]) -> [&'a mut T; N] {
   arr.map(|ptr| unsafe { &mut *ptr })
}

but since that is not set to stabilize by 1.51 (as far as I know) I do not wish to use it.

Upvotes: 3

Views: 741

Answers (3)

Freyja
Freyja

Reputation: 40784

Ideally, you would just use std::mem::transmute here, as references and pointers have the same memory layout. Unfortunately, std::mem::transmute doesn't work on generic arrays, even when the two types have the same layout. However, you can use union to work around this, as using a #[repr(C)] union is analogous to transmute:

#[repr(C)]
union Transmute<'a, T, const N: usize> {
    ptr_arr: ManuallyDrop<[*mut T; N]>,
    ref_arr: ManuallyDrop<[&'a mut T; N]>,
}

unsafe fn ptrs_to_refs<'a, T, const N: usize>(ptrs: [*mut T; N]) -> [&'a mut T; N] {
    let u = Transmute::<'a, T, N> { ptr_arr: ManuallyDrop::new(ptrs) };
    ManuallyDrop::into_inner(u.ref_arr)
}

You can also use std::mem::transmute_copy, which effectively does the same here:

unsafe fn ptrs_to_refs<'a, T, const N: usize>(ptrs: [*mut T; N]) -> [&'a mut T; N] {
    std::mem::transmute_copy(&ptrs)
}

Upvotes: 2

orlp
orlp

Reputation: 117641

Adapting an example from the MaybeUninit documentation:

unsafe fn ptrs_to_refs<'a, T, const N: usize>(ptrs: [*mut T; N]) -> [&'a mut T; N] {
    // Create an uninitialized array of `MaybeUninit`. The `assume_init` is
    // safe because the type we are claiming to have initialized here is a
    // bunch of `MaybeUninit`s, which do not require initialization.
    let mut refs: [MaybeUninit<&'a mut T>; N] = MaybeUninit::uninit().assume_init();
    
    // Dropping a `MaybeUninit` does nothing. Thus using raw pointer
    // assignment instead of `ptr::write` does not cause the old
    // uninitialized value to be dropped.
    for i in 0..N {
        refs[i] = MaybeUninit::new(&mut *ptrs[i]);
    }
    
    std::mem::transmute_copy::<_, [&'a mut T; N]>(&refs)
}

This answer does not rely on whether it's safe to transmute [*mut T; N] to [&mut T; N], but only on MaybeUninit<T> to T after properly initializing.

Upvotes: 2

Shepmaster
Shepmaster

Reputation: 430348

std::mem::transmute would be where I'd reach for first, but that doesn't currently work; see Const generics: Generic array transmutes do not work #61956 for full details.

In the meantime, you can use a union as a workaround, since pointers and references have the same layout:

use core::mem::ManuallyDrop;

unsafe fn ptrs_to_refs<'a, T, const N: usize>(ptrs: [*mut T; N]) -> [&'a mut T; N] {
    union X<'a, T, const N: usize> {
        a_raw: ManuallyDrop<[*mut T; N]>,
        a_ref: ManuallyDrop<[&'a mut T; N]>,
    }

    let a_raw = ManuallyDrop::new(ptrs);
    let x = X { a_raw };
    ManuallyDrop::into_inner(x.a_ref)
}

You should also be able to use transmute_copy to the same effect:

unsafe fn ptrs_to_refs<'a, T, const N: usize>(ptrs: [*mut T; N]) -> [&'a mut T; N] {
    std::mem::transmute_copy(&ptrs)
}

Upvotes: 2

Related Questions