RUST : How To call RUST function from C

4 minute read

How to call RUST function from C

I make programs mainly using C or C++ language. Recently I’m studying Rust. So I wanna interact to C and Rust. I want to link each source file(.c or .rs) after compiling it as an object file.

Let’s say I want to call a Rust function in C.
How do I do this? What does it mean to call a rust function from c? Actually I don’t know about it. So I Searching for google. So many keyword pop out!! What caught my eye was the FFI(Foreign Function Interface).

Let’s implement C function for use in Rust. Plz follow this code.

// file : main.c
#include <stdio.h>

void c_print_fn(const char* str) {
  printf("%s\n", str);
}

int main() {
  printf("Hello World\n");

  // this is test for c
  c_print_fn("Calling in C");

  return 0;
}

When We build and run the program, We’ll see something like this:

$ clang -o main main.c
$ ./main
Hello World
Calling in C

Great! Next step is make Rust source file.
First, let’s make the main function.

// file : rust_ffi.rs
fn call_from_c(num: i32) -> i32 {
  num
}

// Just function test... later delete
fn main() {
  println!("{}", call_from_c(10));
}

We will use only rustc is the compiler for the Rust programming language, provided by the project itself.

$ rustc rust_ffi.rs
$ ./rust_ffi
10

Now step is calling RUST function from C.
Write the function to use for c in pub extern "C" fn ~~~.
At this time, it should be the same as the prototype implemented in C.

// file : rust_ffi.rs
pub extern "C" fn call_from_c(num: i32) -> i32 {
  num
}

// TODO : later remove main function
fn main() {
  println!("{}", rust_pow_num(10));
}

Okay… Let’s call the rust function from C

// file : main.c
#include <stdio.h>

void c_print_fn(const char* str) {
  printf("%s\n", str);
}

int main() {
  printf("Hello World\n");
  
  // this is test for c
  c_print_fn("Calling in C");

  // rust fn call!!
  printf("%d\n", call_from_c(211));

  return 0;
}

When this code(main.c) build, maybe we can see the error.

$ clang -o main main.c
main.c:14:18: warning: implicit declaration of function 'call_from_c' is 
      invalid in C99 [-Wimplicit-function-declaration]
  printf("%d\n", call_from_c(211)); 
                 ^
1 warning generated.
/tmp/main-c995b9.o: In function `main':
main.c:(.text+0x6a): undefined reference to `call_from_c'
clang: error: linker command failed with exit code 1 
(use -v to see invocation)

Compiler doesn’t know where call_from_c function is. So declare function and parameter same as rust function.
fix like this :

// file : main.c
#include <stdio.h>

// declare function name and parameter
int call_from_c(int num);

void c_print_fn(const char* str) {
  printf("%s\n", str);
}

int main() {
  printf("Hello World\n");

  // this is test for c
  c_print_fn("Calling in C");

  // rust fn call!!
  printf("%d\n", call_from_c(211));

  return 0;
}

We will make object files in C and Rust. Then linking togeter. leggo~

$ clang -c main.c
$ rustc --emit=obj rust_ffi.rs

$ clang -o test main.o rust_ffi.o
rust_ffi.o: In function `main':
rust_ffi.7rcbfp3g-cgu.0:(.text.main+0x0): multiple definition of `main'
main.o:main.c:(.text+0x30): first defined here
main.o: In function `main':
main.c:(.text+0x68): undefined reference to `call_from_c'
rust_ffi.o: In function `std::rt::lang_start::h8e054b1a1a6cb597':
rust_ffi.7rcbfp3g-cgu.0:(.text._ZN3std2rt10lang_start17h8e054b1a1a6cb597E+0x34): 
undefined reference to `std::rt::lang_start_internal::h9cf8802361ad86c2'
rust_ffi.o: In function `rust_ffi::main::h7f93501ccd4adba5':
rust_ffi.7rcbfp3g-cgu.0:(.text._ZN8rust_ffi4main17h7f93501ccd4adba5E+0x21): 
undefined reference to `core::fmt::num::imp::_$LT$
impl$u20$core..fmt..Display$u20$for$u20$i32$GT$::fmt::h765415089818841e'
rust_ffi.7rcbfp3g-cgu.0:(.text._ZN8rust_ffi4main17h7f93501ccd4adba5E+0x8b): 
undefined reference to `std::io::stdio::_print::h7e1d4022dd9ebaea'
rust_ffi.o:(.data.DW.ref.rust_eh_personality[DW.ref.rust_eh_personality]+0x0): 
undefined reference to `rust_eh_personality'
clang: error: linker command failed with exit code 1 (use -v to see invocation)

Hum… Why don’t make executable file?… Let’s fix it.
I think rust file(rust_ffi.rs) has a problem… because multiple definition of ‘main’ is very important build error.
rust_ffi.rs file has a main function and comment.
Delete main as comment. like this:

// file : rust_ffi.rs
pub extern "C" fn call_from_c(num: i32) -> i32 {
  num
}
$ rustc --emit=obj rust_ffi.rs

error[E0601]: `main` function not found in crate `rust_ffi`
 --> rust_ffi.rs:1:1
  |
1 | / pub extern "C" fn rust_pow_num(num: i32) -> i32
2 | | {
3 | |     num
4 | | }
  | |_^ consider adding a `main` function to `rust_ffi.rs`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0601`.

This time, error occureed because there is now main function (error[E0601]: main function not found in crate 'rust_ffi') Let me know “main function” isn’t exist in file to RUST using #![no_main] keyword.

// file : rust_ffi.rs
#![no_main]
pub extern "C" fn rust_pow_num(num: i32) -> i32 {
  num
}

Okay… It’s too difficult… check for build.
Oh finally!! Can we complete it successfully?

Let’s linking one more time

$ clang -c main.c
$ rustc --emit=obj rust_ffi.rs

$ clang -o test main.o rust_ffi.o
main.o: In function `main':
main.c:(.text+0x68): undefined reference to `call_from_c'
clang: error: linker command failed with exit code 1 
(use -v to see invocation)

call_from_c function could not be found. Maybe We should declare something more in the rust file.

Oh other sample code has #[no_mangle] keyword.. we will try this..

// file : rust_ffi.rs
#![no_main]
#[no_mangle]
pub extern "C" fn rust_pow_num(num: i32) -> i32 {
  num
}

Please let me succeed 🙏 :(

$ clang -c main.c
$ rustc --emit=obj rust_ffi.rs

$ clang -o test main.o rust_ffi.o
$ ./test 
Hello World
Calling in C
211

Finally, We succeeded and we got the desired result.
Now let’s apply it and learn more about ‘rust’.


reference