Существует библиотека Rust, которая предоставляет три функции:
- конструктор для вектора с плавающей точкой
- функция для переноса числа в вектор
- функция, которая суммирует по вектору
Например:
#[no_mangle]
pub extern "C" fn initialize_vector() -> *mut Vec<f32> {
Box::into_raw(Box::new(Vec::<f32>::new()))
}
#[no_mangle]
pub extern "C" fn push_to_vector(ptr: *mut Vec<f32>, x: f32) -> *mut Vec<f32> {
if ptr.is_null() { return ptr; }
let vector_box = unsafe { Box::from_raw(ptr) };
let mut example_vector = *vector_box;
example_vector.push(x);
Box::into_raw(Box::new(example_vector))
}
#[no_mangle]
pub extern "C" fn sum_vector(ptr: *mut Vec<f32>) -> f32 {
if ptr.is_null() { return 0.0; }
let vector_box = unsafe { Box::from_raw(ptr) };
let example_vector = *vector_box;
return example_vector.iter().sum();
}
initialize_vector
создает Vec
и упаковывает его в Box
, возвращая необработанный указатель назвонящий.Теперь вызывающий абонент отвечает за соответствующее освобождение выделения.
push_to_vector
становится владельцем Box<Vec<f32>>
, перемещает Vec
из Box
, освобождая выделенную памятьна Box
в процессе.Затем он добавляет элемент к вектору, создает новый Box
(с соответствующим распределением) вокруг Vec
и возвращает его.
sum_vector
также становится владельцем Box<Vec<f32>>
, перемещаетVec
из Box
и освобождает выделение памяти.Поскольку владение Vec
не покидает функцию, Rust автоматически освобождает память, связанную с Vec
, когда функция завершается.
Код C #, который использует библиотеку, может выглядеть следующим образом:
using System;
using System.Runtime.InteropServices;
internal class Blabla
{
[DllImport("Example")]
unsafe static public extern IntPtr initialize_vector();
[DllImport("Example")]
unsafe static public extern IntPtr push_to_vector(IntPtr ptr, float x);
[DllImport("Example")]
unsafe static public extern float sum_vector(IntPtr ptr);
static public unsafe void Main()
{
IntPtr vec_ptr = initialize_vector();
vec_ptr = push_to_vector(vec_ptr, (float) 2.2);
vec_ptr = push_to_vector(vec_ptr, (float) 3.3);
float result = sum_vector(vec_ptr);
// is the memory allocated for the vector in Rust freed right now?
Console.WriteLine(string.Format("Result: {0}", result));
Console.ReadLine();
}
}
Как правило, рекомендуется использовать SafeHandle
для завершения указателей, возвращаемых функциями DLL, см., Например, этот вопрос или этот пример .Я понимаю, что цель SafeHandle
состоит в том, чтобы вызывать деструктор в определенных случаях, когда происходит что-то незапланированное.
Поскольку нет необходимости освобождать вектор Rust после вызова функции sum_vector
, все же рекомендуется использовать SafeHandle
в данной ситуации, и если да, то как?Или можно просто оставить код как есть и все нормально?