我正在开发一个.NET C#程序,它通过Rust FFI调用我自己的Rust库,使用extern C关键字并由DllImport加载 .
我这样做是因为我想将复杂的计算委托给Rust . 在从Rust库处理之后,预计会将大量数据返回给C# . 当它在C#中使用时,这些数据应该是 List<MyDataRecord>
.
然后我的问题是:
-
来自Rust,传回这些数据的最佳方法是什么?通过一个指向一个内存块的指针,像一个结构阵列一样排列?
-
C#如何找回这样的内存块指针?这里有线程安全问题吗?
-
如何在C#中快速将这样的内存数据块转换为
List<MyDataRecord>
?
1 回答
我们需要回答的第一个问题是:谁将负责分配和释放内存?如果您事先知道将返回多少元素,那么您可以使用C#代码或Rust代码分配内存 . 如果你没有't know in advance how many elements will be returned, then you have 2 options: 1) have the C# code ask the Rust code how many elements will be returned (if that'可能),那么从C#分配内存; 2)让Rust代码分配内存 . 如果从C#(例如托管阵列)分配托管内存,则可以让垃圾收集器释放内存 . 如果从C#或Rust中分配非托管内存,那么你需要提供一个释放内存的函数 . 请记住,生成结果的Rust代码需要显式"leak"数组/
Vec
,否则你的函数会在返回之前释放内存!你'll find it difficult to use .NET' s
List<T>
类型与非托管代码 . 您应该使用数组 .另一个需要考虑的重要方面是struct 's layout. You' ll必须将
#[repr(C)]
添加到Rust结构中,并将[StructLayout(LayoutKind.Sequential)]
或[StructLayout(LayoutKind.Explicit)]
(取决于结构中的类型)添加到C#结构中,以确保双方以相同的方式布局结构的字段在记忆中 .在C#代码中为Rust函数定义
DllImport
时,可以使用MarshalAs attribute注释参数和返回值,以告诉.NET运行时如何对参数和返回值进行编组或解组 . 特别是,UnmanagedType enumeration有LPArray
成员可能对您的情况有所帮助,但请注意it uses CoTaskMemAlloc and CoTaskMemFree to manage memory . 如果Rust代码负责分配内存,您还可以通过将指针定义为IntPtr
并使用Marshal.PtrToStructure来解组结构并使用Marshal.SizeOf来将指针偏移到下一个元素来手动执行解组 .