我正在使用.NET C#开发一个程序,它通过Rust FFI调用我自己的Rust库,使用extern C关键字并由DllImport加载。
我这样做是因为我想将复杂的计算委托给Rust。在从Rust库处理之后,预计会将大量数据返回给C#。当在C#中使用时,这些数据应该是List<MyDataRecord>
。
然后我的问题是:
List<MyDataRecord>
?答案 0 :(得分:1)
我们需要回答的第一个问题是:谁将负责分配和释放内存?如果您事先知道将返回多少元素,那么您可以使用C#代码或Rust代码分配内存。如果您事先不知道将返回多少元素,那么您有两个选项:1)让C#代码询问Rust代码将返回多少元素(如果可能),然后从C#分配内存; 2)让Rust代码分配内存。如果从C#(例如托管阵列)分配托管内存,则可以让垃圾收集器释放内存。如果从C#或Rust分配非托管内存,则必须使用正确的函数来释放内存。如果使用Rust的默认内存分配器,Rust代码将需要提供释放内存的功能。请记住,生成结果的Rust代码需要显式“泄漏”数组/ Vec
,否则你的函数会在返回之前释放内存!
您会发现很难将.NET的List<T>
类型与非托管代码一起使用。你应该使用数组。
要考虑的另一个重要方面是struct的布局。您必须将#[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
将指针偏移到指针来手动执行解编。下一个元素。