Reputation: 117641
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
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
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
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