7. Building mixed-language programs

This chapter describes how to build a mixed-language program using Ferrocene’s rust compiler.

The examples in this chapter assume the following directory structure:

.
├── c_library
│   └── factorial.c
└── mixed_language
    ├── factorial.rs
    ├── call_factorial_from_c.c
    ├── call_factorial_from_c.h
    └── call_factorial_from_rust.rs

7.1. Foreign library, Rust executable

We start by building a C library for a simple factorial operation. Source file ./c_library/factorial.c contains:

extern int factorial(int value) {
    if (value <= 1) {
        return 1;
    } else {
        return value * factorial(value - 1);
    }
}

To create the library, run:

$ gcc -c -o libfactorial.o factorial.c
$ ar crus libfactorial.a libfactorial.o

This invocation produces library libfactorial.a.

Once the library is produced, we can build a Rust executable that depends on library libfactorial. Source file ./mixed_language/call_factorial_from_rust.rs contains:

extern "C" {
    fn factorial(value: i32) -> i32;
}

fn main() {
    println!("3! = {}", unsafe { factorial(3) });
}

The source file first imports factorial as an external C function. Note that external functions are considered unsafe because the compiler cannot guarantee the soundness of values in non-Rust code. The source file then invokes factorial as a regular Rust function.

To create the executable, run:

$ rustc --edition 2021 -L ../c_library -l factorial call_factorial_from_rust.rs

Note

Depending on the compilation target you want to use, you might need to pass additional flags to rustc. Please check the “Required compiler flags” section of your target’s page.

This invocation produces executable call_factorial_from_rust. Running call_factorial_from_rust should yield:

3! = 6

The compiler is capable of linking a binary to different kinds of libraries, such as static, dynamic, and native libraries. Consult the documentation of compiler arguments -L, and -l. for further details.

7.2. Rust library, foreign executable

We are first going to build a Rust library for a simple factorial operation. Source file ./mixed_language/factorial.rs contains:

use core::ffi::c_int;

#[no_mangle]
pub extern "C" fn factorial(value: c_int) -> c_int {
    match value {
        c_int::MIN ..= 1 => 1,
        _ => value * factorial(value - 1),
    }
}

The source file exports function factorial using the C ABI, and ensures symbol compatibility by using attribute #[no_mangle] and type std::ffi::c_int instead of Rust’s i32.

To create a static library, run:

$ rustc --edition 2021 --crate-type staticlib factorial.rs

Note

Depending on the compilation target you want to use, you might need to pass additional flags to rustc. Please check the “Required compiler flags” section of your target’s page.

This invocation produces library libfactorial.a.

To create a dynamic library, run:

$ rustc --edition 2021 --crate-type cdylib factorial.rs

Note

Depending on the compilation target you want to use, you might need to pass additional flags to rustc. Please check the “Required compiler flags” section of your target’s page.

This invocation produces library libfactorial.so.

We can now build a C executable that depends on library libfactorial. Source file ./mixed_language/call_factorial_from_c.h contains:

int factorial(int value);

Source file ./mixed_language/call_factorial_from_c.c contains:

#include <stdio.h>
#include "call_factorial_from_c.h"

int main() {
    printf("3! = %d\n", factorial(3));
    return 0;
}

To create an executable that links against our static library, run:

$ gcc -o call_factorial_from_c -L . -l factorial call_factorial_from_c.c

Or if you want to create an executable that links against out dynamic library, run:

$ gcc -o call_factorial_from_c -L . -l factorial -Wl,-rpath=. call_factorial_from_c.c

These invocations produce executable call_factorial_from_c. Running call_factorial_from_c should yield:

3! = 6