A simple tutorial on how to call a C function from Rust ๐Ÿฆ€

Overview

How to call a C function from Rust ๐Ÿฆ€ โ˜Ž๏ธ

I was working on a Rust project where we needed to interact with code written in C.

I had to learn how to work with FFI (Foreign Function Interface) in Rust and wrote up this little guide for others.

This repository is a working example of the final code from the tutorial I wrote below. Clone it and run it using cargo run.

$ cargo run
   Compiling rust-ffi-to-c v0.1.0
    Finished dev [unoptimized + debuginfo] target(s) in 0.93s
     Running `target/debug/rust-ffi-to-c`

[Rust] Hello from Rust! ๐Ÿฆ€
[Rust] Calling function in C..
[C] Hello from C!
[C] Input a is: 5000
[C] Input b is: 5
[C] Multiplying and returning result to Rust..
[Rust] Result: 25000

Tutorial

1. Define an external function

We use extern to reference the multiply() function, which is written in C (src/multiply.c).

In this case we want to multiply integers, so we import a C-compatible integer type into Rust from core:ffi. (See all the available types)

We then define the argument types and return type for our C function as c_int (equivalent to i32 in Rust).

extern crate core;
use core::ffi::c_int;

extern "C" {
    fn multiply(a: c_int, b: c_int) -> c_int;
}

2. Call the C function from Rust

Any use of foreign function is considered unsafe because the Rust compiler can't guarantee memory safety in foreign code. So in our main Rust file (src/main.rs) we call the function in an unsafe block, then pass in two i32 integers, and print the result.

unsafe {
    println!("Result: {}", multiply(5000, 5));
}

3. Compile and run

First we compile our multiply.c file using a C compiler:

clang src/multiply.c -c

The -c flag tells the C compiler to output a "object file (.o)" instead of an executable program. So it creates a multiply.o file that we can use as a shared dynamic library in our Rust code.

Then we compile our program using the Rust compiler:

rustc src/main.rs -l multiply.o -L .

The -l multiply.o option tells the Rust compiler to link the shared library. The -L . option tells the Rust compiler to look for libraries in the current directory.

The compiler creates an executable named main which we can run:

./main
Result: 25000

4. Automate ๐Ÿค–

It gets tedious to compile the files manually every time, so we will use cargo build script and the cc crate to automate this process.

Add cc to the projects build dependencies:

[build-dependencies]
cc = "1.0"

Create a build.rs and add compile instructions:

extern crate cc;

fn main() {
    cc::Build::new().file("src/multiply.c").compile("multiply");
}

And now we can use Cargo to build both the C and Rust code and run the program:

cargo run

Notes

  • From Rust 1.64.0 it is now recommended to use core::ffi instead of std::os::raw to access C types. The latter is now an alias to types in the core::ffi module. core is also available in places where the Rust standard library (std) is not, like embedded projects.

  • Mapping out functions manully using extern is fine for small projects, but as soon as you are dealing with a bigger library or codebase, you want to take a look at bindgen. It can automatically generate the bindings for C or C++ libraries, making using them in Rust a lot easier. See the bindgen User Guide.

  • We can control how our code is linked using the #[link()] attribute.. It allows us to specify or rename functions and change the type of linking to use, eg. to static:

    #[link(name = "multiply", kind = "static")]
    extern "C" { // ... }

Further reading

Further watching

You might also like...
(Rust) Coloring terminal so simple you already know how to do it !

Colored Coloring terminal so simple, you already know how to do it! "this is blue".blue(); "this is red".red(); "this is red on blue".red(

A simple CLI pomodoro timer written in Rust.

Pomodoro A simple CLI pomodoro timer written in Rust. Based on the Pomodoro Technique. Works on any platform that supports desktop notifications. Exam

Simple Interactive Terminal Todo App in Rust
Simple Interactive Terminal Todo App in Rust

todo-rs Simple Interactive Terminal Todo App in Rust Quick Start $ cargo run TODO Controls Keys Description k, j Move cursor up and down Shift+K, Shif

Simple test app based on rust-psp

PSP Test App Simple test app based on rust-psp. Demonstrating the usage of C libs. Build Download and unzip the prebuilt PSPSDK (built from clang-psp)

Simple cli clipboard manager written in rust

Simple cli clipboard manager written in rust

Simple system monitoring app that runs on terminal. Made purely with Rust.
Simple system monitoring app that runs on terminal. Made purely with Rust.

What is it? RCTOP is a simple WIP system monitoring app that runs purely on terminal and doesn't feature GUI. One can compare it to htop, but more str

My solutions for the 2021 edition of the Advent of Code, using Rust and SOM (Simple Object Machine)

Advent of Code 2021 These are my solutions for the 2021 edition of the Advent of Code. The solutions are all implemented using both Rust and SOM (Simp

Simple command line flag parser for rust.

easy_flag Simple command line flag parser for rust. use easy_flag::FlagSet; fn main() - Result(), String{ let mut help = false; let mut my

A simple, fast and interruptable download accelerator, written in Rust
A simple, fast and interruptable download accelerator, written in Rust

snatch A simple, fast and interruptable download accelerator, written in Rust WARNING This project is no longer maintained by @k0pernicus and @jean-se

Owner
Vanja Cosic
Developer, designer, hacker. Previously @elastic & @Opbeat. Now: Cybersecurity and privacy consultant. Building tools in Elixir and Rust ๐Ÿฆ€
Vanja Cosic
ddi is a wrapper for dd. It takes all the same arguments, and all it really does is call dd in the background

ddi A safer dd Introduction If you ever used dd, the GNU coreutil that lets you copy data from one file to another, then you may have encountered a ty

Tomรกs Ralph 80 Sep 8, 2022
call-me-maybe is a small CLI tool to notify you of the completion of a command

call-me-maybe call-me-maybe is a small CLI tool to notify you of the completion of a command By default, the tools consumes stdin for a message's cont

Samuel Yvon 4 Sep 16, 2022
Repository containing assets for the Holium CLI tutorial

Welcome to getting-started ?? In this repository you can find all necessary assets for the Holium CLI tutorial ?? Homepage The tutorial that reference

Polyphene 2 Mar 11, 2022
An ebpf knowledge base, based on llama_index and bpf-developer-tutorial

ebpf-knowledge-base An ebpf knowledge base, based on llama_index and bpf-developer-tutorial Usage First, you need to clone this repo: git clone --recu

eunomia-bpf 7 Apr 1, 2023
Safe OCaml-Rust Foreign Function Interface

ocaml-rust This repo contains code for a proof of concept for a safe OCaml-Rust interop inspired by cxx. This is mostly optimized for calling Rust cod

Laurent Mazare 23 Dec 27, 2022
Command line interface as a function.

Command line interface as a function. fncmd fncmd is an opinionated command line parser frontend that wraps around clap. The functionality is mostly i

Yu Shimura 67 Dec 7, 2022
A cargo subcommand that displays ghidra function output through the use of the rizin rz-ghidra project.

cargo-rz-ghidra A cargo subcommand that displays ghidra function output through the use of the rizin rz-ghidra project. Install cargo install --git ht

wcampbell 4 Nov 5, 2022
โšก A Blazing fast alternative to the stock windows folder delete function!

Turbo Delete A blazing fast alternative to the default Windows delete. Turbodelete is a blazing fast alternative to the default Windows delete functio

Tejas Ravishankar 165 Dec 4, 2022
a simple program that you can scrap, is shit and really simple but is cool.

if you want to run it you need to have installed curl by default scrap youtube, but you can change it, also change the number of threads and run: carg

pai 7 Oct 15, 2021
A dead simple ANSI terminal color painting library for Rust.

yansi A dead simple ANSI terminal color painting library for Rust. use yansi::Paint; print!("{} light, {} light!", Paint::green("Green"), Paint::red(

Sergio Benitez 169 Dec 25, 2022