使用 const 泛型将可变指针数组转换为可变引用
Converting array of mutable pointers to mutable references with const generics
假设我保证(通过未指定的方式)我有一个 [*mut T; N]
类型的数组,其中包含 N
有效且不相交的 可变指针,所有生命周期 'a
。如何将其转换为可变引用数组 [&'a mut T; N]
?
也就是说,我该如何实现这个功能?
unsafe fn ptrs_to_refs<'a, T, const N: usize>(ptrs: [*mut T; N]) -> [&'a mut T; N] {
// What goes here?
}
我不是特别在寻找使用分配步骤的实现(例如使用 Vec
)。
因为 min_const_generics
在 Rust 1.51 中是 set to stabilize,这个问题针对的是假设的稳定 Rust,它也有 min_const_generics
。我正在寻找符合这些要求的答案,不使用其他不稳定的功能。
例如使用 array_map
功能,可以简单地
unsafe fn ptrs_to_refs<'a, T, const N: usize>(ptrs: [*mut T; N]) -> [&'a mut T; N] {
arr.map(|ptr| unsafe { &mut *ptr })
}
但是因为它没有设置为稳定到 1.51(据我所知)我不想使用它。
理想情况下,您只需使用 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)
}
您也可以使用 std::mem::transmute_copy
,它在这里有效地执行相同的操作:
unsafe fn ptrs_to_refs<'a, T, const N: usize>(ptrs: [*mut T; N]) -> [&'a mut T; N] {
std::mem::transmute_copy(&ptrs)
}
std::mem::transmute
是我首先要到达的地方,但目前还行不通;有关详细信息,请参阅 Const generics: Generic array transmutes do not work #61956。
在此期间,您可以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)
}
您应该也可以使用 transmute_copy
达到同样的效果:
unsafe fn ptrs_to_refs<'a, T, const N: usize>(ptrs: [*mut T; N]) -> [&'a mut T; N] {
std::mem::transmute_copy(&ptrs)
}
改编 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)
}
这个答案不依赖于将 [*mut T; N]
转换为 [&mut T; N]
是否安全,而是仅在正确初始化后 MaybeUninit<T>
转换为 T
。
假设我保证(通过未指定的方式)我有一个 [*mut T; N]
类型的数组,其中包含 N
有效且不相交的 可变指针,所有生命周期 'a
。如何将其转换为可变引用数组 [&'a mut T; N]
?
也就是说,我该如何实现这个功能?
unsafe fn ptrs_to_refs<'a, T, const N: usize>(ptrs: [*mut T; N]) -> [&'a mut T; N] {
// What goes here?
}
我不是特别在寻找使用分配步骤的实现(例如使用 Vec
)。
因为 min_const_generics
在 Rust 1.51 中是 set to stabilize,这个问题针对的是假设的稳定 Rust,它也有 min_const_generics
。我正在寻找符合这些要求的答案,不使用其他不稳定的功能。
例如使用 array_map
功能,可以简单地
unsafe fn ptrs_to_refs<'a, T, const N: usize>(ptrs: [*mut T; N]) -> [&'a mut T; N] {
arr.map(|ptr| unsafe { &mut *ptr })
}
但是因为它没有设置为稳定到 1.51(据我所知)我不想使用它。
理想情况下,您只需使用 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)
}
您也可以使用 std::mem::transmute_copy
,它在这里有效地执行相同的操作:
unsafe fn ptrs_to_refs<'a, T, const N: usize>(ptrs: [*mut T; N]) -> [&'a mut T; N] {
std::mem::transmute_copy(&ptrs)
}
std::mem::transmute
是我首先要到达的地方,但目前还行不通;有关详细信息,请参阅 Const generics: Generic array transmutes do not work #61956。
在此期间,您可以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)
}
您应该也可以使用 transmute_copy
达到同样的效果:
unsafe fn ptrs_to_refs<'a, T, const N: usize>(ptrs: [*mut T; N]) -> [&'a mut T; N] {
std::mem::transmute_copy(&ptrs)
}
改编 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)
}
这个答案不依赖于将 [*mut T; N]
转换为 [&mut T; N]
是否安全,而是仅在正确初始化后 MaybeUninit<T>
转换为 T
。