10

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.

1
  • 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. Commented Mar 21, 2015 at 14:12

2 Answers 2

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.

Sign up to request clarification or add additional context in comments.

5 Comments

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 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.
@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 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.
@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). ^_^
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]
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.