How to return a vector from c back to rust [FFI]

C++

extern "C" {
    void query (uchar * * res, size_t * rln)
    {
        *rln = 20;
        *res = new uchar[*rln]; 
        for (size_t i = 0; i < rln; ++i) {
            (*res)[i] = 'x';
        }
    }

    void free_res (uchar * res)
    {
        delete[] res;
    }
}

Rust

mod ffi {
    use ::libc::{c_uchar, size_t};

    extern "C" {
        fn query (res: &mut *mut c_uchar, rln: &mut size_t)
        ;
    }

    // Create safe API around this:
    use ::core::{
        convert::TryInto,
        ops::{Deref, DerefMut},
        ptr,
        slice,
    };

    pub(in super)
    struct CppBoxedSlice {
        ptr: ptr::NonNull<c_uchar>,
        len: usize,
    }

    impl CppBoxedSlice {
        #[inline]
        pub
        fn from_query () -> Self
        {
            let mut res: *mut c_uchar = ptr::null_mut();
            let mut rln: size_t = 0;
            unsafe { query(&mut res, &mut rln); }
            let ptr: ptr::NonNull<c_uchar> =
                ptr::NonNull::new(ptr)
                    .expect("Error from `query()`: got NULL ptr")
            ;
            let len: usize =
                len .try_into()
                    .expect("Error from `query()`: `rln` overflowed")
            ;
            Self { ptr, len }
        }
    }

    impl Drop for CppBoxedSlice {
        fn drop (self: &'_ mut Self)
        {
            unsafe { free_res(self.ptr.as_ptr()); }
        }
    }

    impl Deref for CppBoxedSlice {
        type Target = [c_uchar];

        #[inline]
        fn deref (self: &'_ Self) -> &'_ Self::Target
        { unsafe {
            slice::from_raw_parts(self.ptr.as_ptr(), self.len)
        }}
    }
    impl DerefMut for CppBoxedSlice {
        #[inline]
        fn deref_mut (self: &'_ mut Self) -> &'_ mut Self::Target
        { unsafe {
            slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len)
        }}
    }
}
// pub
use ffi::CppBoxedSlice;

let mut result = CppBoxedSlice::from_query(); // <- Owns the allocation -+
let slice: &mut [c_uchar] = &mut *result;                             // |
/* result.drop(); */ // <-- calls free_res() to deallocate --------------+
1 Like