Tuesday, August 27, 2019

FFI tricks in Rust programming Language

Welcome file

How to do a trick on your Rust compiler to allow external (Foreign Language - FFI) functions

Rust is cool programming language which you can easily plugin to other languages ( Foreign Languages) .

Some of the FFI (Foreign language Interface) methods to call rust programs from C programming and Python programming languages are explained here.

Rust - Two things you should know for allowing Foreign language to call

#[no_mangle]
You might see lot of libraries using #[no_mangle] procedural macro.
The #[no_mangle] inform the Rust compiler that this is foreign language function and not to do anything weird with the symbols of this function when compiled because this will called from from other languages. This is needed if you plan on doing any FFI. Not doing so means you won’t be able to reference it in other languages.

extern

The extern keyword is used in two places in Rust. One is in conjunction with the crate keyword to make your Rust code aware of other Rust crates in your project, i.e. extern crate lazy static. The other use is in foreign function interfaces (FFI).

read more extern

how to create a shared object ie .so file from your Rust program ?

We can create a library in Rust using command below command

We are required to update the Cargo.toml

[dependencies]
[lib]
name = "callc"
crate-type = ["staticlib", "cdylib"]

cdylib is important crate type to specifiy in the Cargo.toml.
--crate-type=cdylib, #[crate_type = "cdylib"] - A dynamic system library will be produced. This is used when compiling a dynamic library to be loaded from another language. This output type will create *.so files on Linux, *.dylib files on macOS, and *.dll files on Windows.

Read more on this here linkage

Cargo new callc


//lib.rs
#[no_mangle]
pub extern fn hello(){
  println!("hello world inside rust ");
}
          

as we discussed earlier , since we use #[no_mangle], we can directly go ahead and compile it.
cargo build --release

How to check the generate .so file ?

A file with extension “.so” will generated in you target->release library.
enter image description here

run the below command to verify the function in the shared object.
$nm -D ‘filename.so’ | grep ‘function_name’
enter image description here

Our external function name is available in the shared object file created.

Create a C program for calling the shared object ( .so file)

create C program to call the hello function in your ‘src’ folder.

hello.c

#include<stdio.h>
#include "hello.h" /*header file to be included

/* the main C program to call the hello function
/* hello function is resides in the .so ( shared object) file

int main(void){
   hello();
}

Below header file needs to be created in your ‘src’ folder

hello.h

/*the header include file should have the function declaration.

void hello();

We should link the .so file while compiling the c program. The below command will link and compile the program.

While compiling the C program, link the .so file the ‘release’ folder.

gcc -o hello hello.c -L. -Isrc /home/naveen/rustprojects/callc/target/release/libcallc.so

Your C program should get compiled and 'hello’e executable should be generated.

How do you call a Rust program from Python.

We are going to use the crate pyo3 for creating python modules.

create the projectfolder using cargo
cargo projectname --lib

enter image description here

Your Cargo.toml file should be updated as below in library and dependency sections.

[lib]
name = "string_sum"
crate_type = ["cdylib"]

[dependencies.pyo3]
 version = "0.7.0"
 features = ["extension-module"]

update the lib.rs with the below code:

use pyo3::prelude::*;
use pyo3::wrap_pyfunction;

#[pyfunction]
///format the 2 numbers as string
fn sum_as_string(x:usize,y:usize) -> PyResult<String>{
    Ok((x+y).to_string())
}

#[pymodule]
fn string_sum(py:Python,m:&PyModule) -> PyResult<()>{
    m.add_wrapped(wrap_pyfunction!(sum_as_string))?;
    Ok(())
}

You can build the Rust program using Cargo build --release

This will create .so object in your cargo release folder. You might need to rename the file libstring_sum.so to string_sum.so

enter image description here

Now you can start import the module to the python interpreter and starting using the function string_sum

You might need to use python3 or 3+ interpreters for pyo3 to work.
As screenshot shows below , you can import the functions defined in the Rust module and use them in python.

enter image description here

No comments: