Calling native code from Rust – a very simple example!


I have been playing around with Rust for a bit, and so far so good! I think that people make a bit too much of the whole complexity of the Ownership, Borrowing, and Lifetimes concepts. However, I did find the documentation (in the Rust Book, as also in Rust By Example) on Lifetimes to be not only very sparse, but more confusing than helpful. That part could certainly do with some work.

Niko Matsakis has started a small site to help explain some core concepts which I found to be interesting, useful, and fun! You can find them here:Into Rust. It is still a work in progress, and hopefully they will add many more videos – especially on Lifetimes, Unsafe, and I/O (which is a a bit of a bugbear for me right now).

In any case, for this short tutorial, I will attempt to demonstrate how your custom C++ library may be invoked from Rust. Well, let’s get on with it!

Setting up the native library

Just to make things interesting, let us have a native library that takes in a vector of numbers, sorts them in non-increasing order, and returns them to the caller.

Let’s define the C++ functionality in a file called sorting.cpp, with the declaration in the header file sorting.h:

#ifndef __SORTING_H__
#define __SORTING_H__ "sorting.h"

#include <iostream>
#include <functional>
#include <algorithm>

#ifdef __cplusplus
extern "C" {
#endif
  void interop_sort(int[], size_t);
#ifdef __cplusplus
}
#endif
#endif
#include "sorting.h"

void interop_sort(int numbers[], size_t size)
{
  int* start = &numbers[0];
  int* end = &numbers[0] + size;

  std::sort(start, end, [](int x, int y) {  return x > y; });
}

Let’s wrap this up in a nice static library called libmysorting.a (note that I am building this on a macOS system. Please consult your local OS’ documentation for details specific to your platform):

Timmys-MacBook-Pro:Rust_C_Interop z0ltan$ g++ -Wall -std=c++14 -c sorting.cpp
clang: warning: treating 'c' input as 'c++' when in C++ mode, this behavior is deprecated

Timmys-MacBook-Pro:Rust_C_Interop z0ltan$ ar rc libmysorting.a sorting.o
Timmys-MacBook-Pro:Rust_C_Interop z0ltan$ ls
libmysorting.a	sorting.cpp	sorting.h	sorting.o

And just to make sure that no name-mangling has occurred during the process:

Timmys-MacBook-Pro:Rust_C_Interop z0ltan$ nm -gU libmysorting.a 

libmysorting.a(sorting.o):
0000000000000000 T _interop_sort

Okay, looks like everything is in order. Now let’s move on to the Rust bit.

Calling the native library from Rust

To use the library from Rust, let’s create a new Cargo project called sorting_demo:

Timmys-MacBook-Pro:Rust_C_Interop z0ltan$ cargo new --bin sorting_demo
     Created binary (application) `sorting_demo` project

Timmys-MacBook-Pro:Rust_C_Interop z0ltan$ cd sorting_demo

Timmys-MacBook-Pro:sorting_demo z0ltan$ ls
Cargo.toml	src

Now we need to have a way to link the native library into our Rust project. For simplicity’s sake, let’s use the simplest approach:

Timmys-MacBook-Pro:sorting_demo z0ltan$ cp ../libmysorting.a .

Timmys-MacBook-Pro:sorting_demo z0ltan$ ls
Cargo.lock	Cargo.toml	libmysorting.a	src		target

All we did here is to copy libmysorting.a into the root of the Rust project, and set the LD_LIBRARY_PATH environment variable to ensure that the linker can find the library.

Now that that’s set up, let’s write up the logic to link the static library from Rust code. Put the following code inside src/main.rs

#[link(name = "mysorting")]
extern {
    fn interop_sort(arr: &[i32;10], n: u32);
}

pub fn sort_from_cpp(arr: &[i32;10], n: u32) {
    unsafe {
        interop_sort(arr, n);
    }
}

fn main() {
    let my_arr = [100, 200, -99, 34, 56, -656, 99, 94, 84, 89];

    println!("Before sorting...");
    println!("{:?}\n", my_arr);

    sort_from_cpp(&my_arr, 10);

    println!("After sorting...");
    println!("{:?}", my_arr);

}

All right. Now all that remains is to see if this whole setup actually works!

Test spin

Timmys-MacBook-Pro:sorting_demo z0ltan$ cargo run
   Compiling sorting_demo v0.1.0 (file:///Users/z0ltan/Rabota/Blogs/Rust_C_Interop/sorting_demo)
    Finished debug [unoptimized + debuginfo] target(s) in 0.29 secs
     Running `target/debug/sorting_demo`
Before sorting...
[100, 200, -99, 34, 56, -656, 99, 94, 84, 89]

After sorting...
[200, 100, 99, 94, 89, 84, 56, 34, -99, -656]

Nice! So it all works as expected. The array has been correctly sorted in non-increasing order.

Explanation: The code is pretty much self-explanatory. First up we link the static library using the #[link(name = "mysorting")] directive (similar to annotations in other languages). This allows Rust to seek out a library named libmysorting.a or libmysorting.dylib.

Then we simply define the equivalent C++ function, interop_sort along with matching Rust types for the signature (note how we can use an array slice, &[i32;10] to match up with the C array, int arr[10]. Likewise, the size of the array is an unsigned integer, so we declare that to be of type u32. The whole native function itself is wrapped up inside an extern block. There are various options for the extern, and it defaults to extern "C". This block keyword is used to signify foreign code in Rust.

Finally, it’s good practice to wrap up the unsafe function inside a safe Rust function which is what we did with the sort_from_cpp function.

And there you go! A bare minimum working example of calling C++ (or indeed C) code from Rust. A special note here: as we can see, we defined a C interface for the C++ code to avoid name-mangling. If this were not done so directly in the C++ code, we could still have defined a C wrapper (as we have done in other interop examples) and have a callback function from C to the C++ code. That would work as well.

Advertisements
Calling native code from Rust – a very simple example!

Speak your mind!

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s