如何将指向 C++ 函数中分配的数组的指针转换为 Rust 中的数组?

How can I convert a pointer to an array allocated in a C++ function into an array in Rust?

我正在编写与 C++ 接口的 Rust 代码 API。我正在调用一个特定的 C++ 函数,该函数分配一些内存然后 returns 它给我。该函数具有以下签名:

pub unsafe extern "C" fn native_fn(
    count: c_uint, 
    data: *mut *mut NativeStructType
)

它在其中分配 count 类型 NativeStructType 的项目,然后修改给定的指针以指向指向这些对象的指针数组。我这样称呼它:

let mut ptr = 0 as *mut NativeStructType;
native_fn(2, &mut ptr);

我想做这样的事情:

let data : [*mut NativeStructType; 2] = convert(ptr);

但是我该如何进行转换呢?这基本上与.as_ptr()相反。

以上是简化的;我试图调用的实际函数是 z3_sys::Z3_mk_tuple_sort ,我试图在其中解码 proj_decl 输出参数(忽略有关用户需要分配内存的文档,据我所知这是不正确的告诉)。

首先,对您提出的问题进行一般性回答,这可能不适用于您遇到的问题

您需要分两个阶段进行转换。首先,根据您对起始指针和长度的了解创建一个切片:

let data_slice_ref = std::slice::from_raw_parts_mut(ptr, 2);

这一步有很多安全要求——您要获取非 Rust 值并将其解释为 Rust 值。特别是,对于这个 _mut 版本,您需要确保指针确实指向 2 个连续分配(并正确初始化)的 *mut NativeStructTypes 属于同一分配并且将存在足够长的时间,指针正确对齐,并且切片中的字节数将适合 usize。最后一个在这里不是问题,其他要求有望得到 C++ 库的支持(但您确实需要验证它们)。此外,非常重要的是,您需要确保对切片内容的所有读写都将通过您创建的 &mut 引用进行,只要它存在(这是一般安全要求所有 &mut 参考);这既是对 C++ 代码的要求(假设它给了你一个指向全局变量的指针,那么该全局变量将需要被其他代码更改),也是对你的 Rust 代码的要求(现在你有了 &mut参考,你不能通过任何其他方式访问切片后面的字节。

获得已知长度的切片后,可以通过 TryInto:

将其转换为数组
use std::convert::TryInto;
let data: [*mut NativeStructType; 2] = data_slice_ref.try_into().unwrap();

(这里使用 unwrap() 而不是正确的错误处理例程是安全的,因为我们创建的切片的长度为 2,因此转换永远不会产生错误;编译器的代码生成器将优化错误检查out,但仍然需要 unwrap(),这样类型检查器就不会抱怨。)这会将切片的内容复制到一个数组中,并且是合法的,因为原始指针是 Copy;如果您使用的值可能具有非平凡的析构函数或克隆规则,则更好的选择是借用而不是复制它们,方法是使用 &mut [*mut NativeStructType; 2] 上的 TryInto 实现(尽管您必须使非常确定他们会活得足够长)。


无论如何,我怀疑您对 Z3_mk_tuple_sort 工作原理的描述可能不正确,原因有一个:生命周期。在 C++ 和 Rust 中,当你通过原始指针 API 分配某些东西时,你必须知道它分配了多长时间,并在完成后释放它。 from_raw_parts_mut 和它的不可变朋友 from_raw_parts,不知道它正在创建的借用切片的生命周期应该是多少(因为它们是从一个没有生命周期信息的原始指针借用的); Rust 会相信您的 unsafe 断言,即您提供的原始指针将存在足够长的时间,并且只要您尝试使用它,就会将切片视为持久的。但是,从 C++ 的角度来看,生命周期是多少?确保值的生命周期足够长在 C++ 和 Rust 中都很重要,但文档没有提到显然正在分配的缓冲区的生命周期。

文档中的描述说缓冲区是调用者提供的,而不是新分配的,这样更有意义;在那种情况下,您根本不需要这种转换(您可以只在 &mut [*mut NativeStructType; 2] 上使用 as_mut_ptr 并将其传递给 native_fn)。如果它实际上是新分配的,那么大概它会持续“直到释放”,这意味着你必须弄清楚适当的释放函数是什么,并在你完成复制(或借用)你的新建的切片。另一种可能性是它返回一个指向某种静态缓冲区的指针(这在 Rust 中是非常糟糕的做法,在 C++ 中也是糟糕的做法,但偶尔也会看到),在这种情况下你需要非常小心地制作确保在缓冲区被覆盖之前停止使用 &mut 借用。无论哪种方式,您对本机函数工作方式的描述都不正确或不完整,因此,在您弄清楚确切的行为是什么之前,我根据您的描述做出的回答也可能最终对您不起作用。