Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Returning array from Rust to FFI

Tags:

rust

ffi

I need to write a function that returns array of u16 integers in Rust. This function then should be used by FFI.

extern crate libc;
use libc::{uint16_t};

#[no_mangle]
pub extern fn ffi_test() ->  *const uint16_t {
    let test: [u16;4] = [1,2,3,4];

    test.as_ptr()
}

Rust code compiles without errors. I used Ruby to test the ffi call:

# coding: utf-8
require 'ffi'

module MyMod
  extend FFI::Library
  ffi_lib 'my_ffi_test_lib'
  attach_function :ffi_test, [], :pointer
end

a_ptr = MyMod.ffi_test
size = 4
result_array = a_ptr.read_array_of_uint16(size)
p result_array

But the results are totally wrong (expected: [1, 2, 3, 4]):

$ ruby ffi_test.rb
[57871, 25191, 32767, 0]

As if I am reading totally diffirent memory addr. I assume maybe that I should not use #as_ptr() on Rust array?

EDIT

As per recommendation of @FrenchBoiethios I tried to box the array:

extern crate libc;
use libc::{uint16_t};

#[no_mangle]
pub extern fn ffi_test() ->  *mut uint16_t {
    let test: [u16;4] = [1,2,3,4];

    let b = Box::new(test);
    Box::into_raw(b)
}

This gives compile error:

note: expected type `std::boxed::Box<u16>`
         found type `std::boxed::Box<[u16; 4]>`
like image 347
Ernest Avatar asked Aug 22 '19 19:08

Ernest


People also ask

How do I return an array in Rust?

Adapting your function, and still returning an array, requires specifying the number of elements in the return type i.e. fn generateArray()->[f64; 100] . The rust type system accounts for the size of an array, so [f64; 100] (an array of length 100 containing f64 s) is a different type from [f64; 99] .

What is FFI in Rust?

This module provides utilities to handle data across non-Rust interfaces, like other programming languages and the underlying operating system. It is mainly of use for FFI (Foreign Function Interface) bindings and code that needs to exchange C-like strings with other languages.


1 Answers

Your array is on the stack, so there is a lifetime issue when you returns it as a pointer (returned pointer to a local variable). You must allocate it in the heap:

#[no_mangle]
pub extern "C" fn ffi_test() -> *mut u16 {
    let mut test = vec![1, 2, 3, 4];
    let ptr = test.as_mut_ptr();
    
    std::mem::forget(test); // so that it is not destructed at the end of the scope
    
    ptr
}

or

#[no_mangle]
pub extern "C" fn ffi_test() -> *mut u16 {
    let test = Box::new([1u16, 2, 3, 4]);  // type must be explicit here...

    Box::into_raw(test) as *mut _          // ... because this cast can convert
                                           // *mut [i32; 4] to *mut u16
}
like image 164
Boiethios Avatar answered Oct 13 '22 19:10

Boiethios