当前位置: 首页 > 知识库问答 >
问题:

使用常量泛型将可变指针数组转换为可变引用

井疏珂
2023-03-14

假设我保证(通过未指定的方式)我有一个[*mut;N]类型的数组,其中包含N有效且不相交的可变指针,所有指针都具有生存期'a。如何将其转换为可变引用数组[

换句话说,我如何实现这个功能?

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中稳定,所以这个问题针对的是一个假设的稳定Rust,它也具有min_const_generics。我正在寻找与这些要求兼容的答案,不使用其他不稳定的功能。

例如,使用功能array\u 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(据我所知),我不希望使用它。


共有3个答案

章松
2023-03-14

改编maybeuniit文档中的一个示例:

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]转换为[是否安全

诸葛立果
2023-03-14

std::mem::transmute是我首先到达的地方,但目前不起作用;有关完整详细信息,请参阅Const泛型:泛型数组transmute不起作用#61956。

同时,可以使用联合作为解决方案,因为指针和引用具有相同的布局:

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)
}
曹渝
2023-03-14

理想情况下,您可以在此处使用std::mem::transmute,因为引用和指针具有相同的内存布局。不幸的是,std::mem::transmute不适用于通用数组,即使两种类型具有相同的布局。但是,您可以使用Union来解决这个问题,因为使用#[repr(C)]联合类似于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)
}
 类似资料:
  • 当使用来自外部库的函数时,“任意”取消引用指向const引用的指针是否安全? 这样做是否普遍安全/明智 更重要的是,除了C语言中“不赞成”的原始指针之外,使用常量指针真的有充分的理由这样做吗?

  • 问题内容: 我目前正在尝试将某些行PIVOT转换为列。问题是我并不总是知道有多少行可用。让我们看一个例子: 结果” 静态枢纽的SQL FIDDLE范例。我正在尝试实现动态枢轴-http://sqlfiddle.com/#!3/ 2be82/1 因此,这是我的难题:在这种情况下,我希望能够基于GroupID透视未知数量的列。 我希望能够将GroupID 3中的所有行都列为PIVOT。我需要在不知道g

  • 问题 你已经获得了一个被编译函数的内存地址,想将它转换成一个Python可调用对象, 这样的话你就可以将它作为一个扩展函数使用了。 解决方案 ctypes 模块可被用来创建包装任意内存地址的Python可调用对象。 下面的例子演示了怎样获取C函数的原始、底层地址,以及如何将其转换为一个可调用对象: >>> import ctypes >>> lib = ctypes.cdll.LoadLibrar

  • 我创建了2个函数,读取和写入一个路径,声明为: 我创建了一个额外的函数,它将使用路径调用上述函数之一 但是,当作为回调传递给时,编译器会发出以下警告 将“int(const char*,const void,int)”传递给类型为“int()(const char*,void*,int)”的参数的指针类型不兼容 将接受常量指针的函数转换为接受非常量指针的函数会是未定义的行为吗? 我注意到有一个几乎

  • 我得到以下错误: 初始化中无法将“std::__cxx11::basic_string”转换为“int” 有办法解决这个问题吗?我想把AG变成一个int,这样它就可以被传递到int参数中,或者如果它是int,就可以被测试;或者能够将其值赋给int

  • 为什么允许使用memcpy指针更改常量变量? 此代码: 编译时只包含一个警告: 警告:传递“memcpy”的参数 1 将丢弃指针目标类型中的“const”限定符 [默认启用] 但运行得很好,并更改了常量变量的值。