9

I'm trying to make a Rust dylib and use it from other languages, like C, Python and others. I've successfully called a Rust function taking an i32 argument from Python. Now I'm trying to make a function that takes an array (or a pointer to it, or whatever is necessary to pass a dataset to Rust).

#![crate_type = "dylib"]
#[no_mangle]
pub extern "C" fn rust_multiply(size: i32, arrayPointer: &i32) -> i32 {
    *(arrayPointer)
}

This works as expected. But

#![crate_type = "dylib"]
#[no_mangle]
pub extern "C" fn rust_multiply(size: i32, arrayPointer: &i32) -> i32 {
    *(arrayPointer + 1) // trying to get next element
}

fails with

error[E0614]: type `i32` cannot be dereferenced
 --> src/lib.rs:4:5
  |
4 |     *(arrayPointer + 1) // trying to get next element
  |     ^^^^^^^^^^^^^^^^^^^

Doing this:

pub extern fn rust_multiply(size: i32, array: &[i32]) -> i32

and doing something like array[0] fails with "length = 0" error.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
aspcartman
  • 215
  • 5
  • 9
  • 1
    You probably don't want to have `extern` functions that accept `i32` or references (such as `&i32`). It's better to use the C types that are guaranteed to match your platform - `libc::uint32_t` as shown in the answer. Also, Rust references are *guaranteed* to be non-NULL, but there's nothing enforcing that when you cal it via FFI. It'd be safer to accept a `*const libc::uint32_t` (again, as shown in the answer) and then assert it is non-NULL before making it into a reference. – Shepmaster Mar 21 '15 at 14:12

2 Answers2

12

You have to make some efforts to provide a pure C API and implement some conversions using unsafe code. Fortunately, it is not so difficult:

extern crate libc;

#[no_mangle]
pub extern "C" fn rust_multiply(
    size: libc::size_t,
    array_pointer: *const libc::uint32_t,
) -> libc::uint32_t {
    internal_rust_multiply(unsafe {
        std::slice::from_raw_parts(array_pointer as *const i32, size as usize)
    }) as libc::uint32_t
}

fn internal_rust_multiply(array: &[i32]) -> i32 {
    assert!(!array.is_empty());
    array[0]
}

There is a good introduction for Rust FFI on the official site.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
swizard
  • 2,551
  • 1
  • 18
  • 26
  • 4
    Note that when using an unsafe block, it's **up to the programmer** to verify that the code within meets all the Rust requirements for safety. In this example, references can **never** be `NULL`, so you should really have an assertion that `array_pointer` is not `NULL` (unless you somehow can guarantee it never will be, which is even tougher). – Shepmaster Mar 21 '15 at 13:35
  • @Shepmaster That comes with the territory (C FFI) though. And note that `&` pointers must be non-null as well, so if it can ever be null, the signature in OP's code is wrong too. –  Mar 21 '15 at 14:03
  • 2
    @delnan I might be misunderstanding your point. My point is that `*const libc::uint32_t` is allowed to be NULL, because that's perfectly valid C, as you mention. This code converts it to a slice, which *doesn't allow* the data pointer to be NULL (not sure if the length comes into play — does it matter if the data is NULL when then length is `0`?). I'm simply saying there should be an `assert!(array_pointer != std::ptr::null())` before calling `from_raw_parts`. This is all about protecting ourselves from crazy C callers as best we can. – Shepmaster Mar 21 '15 at 14:09
  • @Shepmaster Sure, I agree. I was trying to add that (1) the NULL problem already exists in the question, and (2) some dangers can never be ruled out in Rust when interacting with C. The second is kind of besides the point, I have to admit in hindsight. –  Mar 21 '15 at 14:14
  • @delnan Absolutely, great points. I commented on OP to address (1). There's not much we can do about (2), sadly. The C FFI consumer can always pass in `(-1, 0xDEADBEEF)`. ^_^ – Shepmaster Mar 21 '15 at 14:17
0

@swizard's answer gets unsigned integers and converts them to signed integers. The code you probably want is

extern crate libc;

#[no_mangle]

pub extern "C" fn rust_multiply(
    size: libc::size_t,
    array_pointer: *const libc::int32_t,
) -> libc::int32_t {
    internal_rust_multiply(unsafe {
        std::slice::from_raw_parts(array_pointer as *const i32, size as usize)
    }) as libc::int32_t
}

fn internal_rust_multiply(array: &[i32]) -> i32 {
    assert!(!array.is_empty());
    array[0]
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Joels Elf
  • 714
  • 6
  • 10