0

I want to return a f64 vector from Rust to Python and I'm not sure how to dereference the returned pointer in Python side.

lib.rs

use libc::c_double;

#[no_mangle]
pub extern "C" fn give_me_array() -> *const Vec<c_double> {

    let array_to_pass = vec![1.05, 1.07, 2.48];    // Just an example... there are some calculations here

    &array_to_pass

}

requesting_array.py

from cffi import FFI
ffi = FFI()
ffi.cdef("""
    double* give_me_array();
""")

dll = ffi.dlopen("...path_to_dll...")
returned_array = dll.give_me_array()
print(returned_array)

The output is a memory location:

<cdata 'double *' 0x00000....>

Expected output: 1.05, 1.07, 2.48

How to dereference the array in the python side? Is this the best approach to pass a variable sized vector from Rust to Python?

Rust-side solution by Aiden4 and modifications on Python side

lib.rs

use libc; // 0.2.93

#[repr(C)]
pub struct CDoubleArray {
    pub ptr: *mut libc::c_double,
    pub len: usize,
}
#[no_mangle]
pub extern "C" fn give_me_array() -> CDoubleArray {
    let vec = vec![1.05, 1.07, 2.48];
    let len = vec.len();
    CDoubleArray {
        ptr: vec.leak().as_mut_ptr(), //leaked so the caller is responsible for the memory
        len,
    }
}
#[no_mangle]
pub unsafe extern "C" fn free_array(arr: CDoubleArray) {
    // since the caller is responsible for the memory we have to provide a way to free it
    Vec::from_raw_parts(arr.ptr, arr.len, arr.len);
}

requesting_array.py

from cffi import FFI
ffi = FFI()
ffi.cdef("""

    typedef struct {
        double *v2_ptr;
        unsigned long v2_size;
    } CDoubleArray;

    CDoubleArray give_me_array();
    void free_array(CDoubleArray);
    
""")

dll = ffi.dlopen("...path_to.dll...")
returned_array = ffi.new("CDoubleArray *")
returned_array = dll.give_me_array()

for n in range(returned_array.v2_size):
    print(returned_array.v2_ptr[n])
mbfernan
  • 13
  • 5
  • I don't know enough about ffi to answer your question but you should never return a `Vec` (or a pointer to one). You should be instead returning a pointer to the raw data and the length, and putting it back together with python's appropriate data type. – Aiden4 May 16 '21 at 15:29
  • @Aiden4 why not? How to implement your suggestion? Resturning a struct with the pointer and a usize? – mbfernan May 16 '21 at 17:00
  • 1
    Only `#[repr(C)]` structs and primitive types are ffi safe because only they provide layout guarantees. `Vec` isn't one of those, and there is no guarantee that even `Vec` and `Vec` are the same. And yes, returning a `#Repr(C)]` struct with a pointer and a length would be the best way. – Aiden4 May 16 '21 at 18:20
  • Can you give an example on how to implement that? – mbfernan May 16 '21 at 18:28
  • The rust side might look like [this](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=5a9c3191115af85f1ad1d896af78b0f0) – Aiden4 May 16 '21 at 19:30
  • Thank you! I edited the solution. It worked well for me. – mbfernan May 16 '21 at 21:19

0 Answers0