I've been learning how to export functions from a rust library, to be consumed by other applications, written in other languages. I've been following the documentation on FFI in rust.
I have it working. I've created a library that exports two functions. Both with the #[no_mangle] attribute. I know the #[no_mangle] attribute keeps the function names clear, just by using a tool like nm to see the exported symbols.
I made one function with extern "C" and one without. Both appear to function the same when called from C and python.
I was expecting the extern "C" keywords changed how data for the functions was pushed on the stack. And therefore expecting the functions to behave differently when called from different applications.
So, my question: Why the need for extern "C" keywords? and what does it do exactly?
According to the documentation, The "C" part defines which application binary interface (ABI) the external function uses---the ABI defines how to call the function at the assembly level. The "C" ABI is the most common, and follows the C programming language’s ABI.
So other than conforming to a specification, what does that mean?
lib.rs
#[no_mangle]
pub fn not_extern_c(x : i32, y : i32) -> i32 {
if x < 0 {
return y;
}
return x;
}
#[no_mangle]
pub extern "C" fn correct_extern_c(x : i32, y : i32) -> i32 {
if x < 0 {
return y;
}
return x;
}
In C, I call these methods like this:
runlib.c
#include <stdint.h>
#include <stdio.h>
int32_t not_extern_c(int32_t, int32_t);
int32_t correct_extern_c(int32_t, int32_t);
int main() {
printf("\nshowing results of incorrect and correct use of extern \"C\" functions\n");
printf("using 'not_extern_c(-1, 4)' output %d\t expected 4\n", not_extern_c(-1, 4));
printf("using 'not_extern_c(11, 4)' output %d\t expected 11\n", not_extern_c(11, 4));
printf("using 'correct_extern_c(-1, 4)' output %d\t expected 4\n", correct_extern_c(-1, 4));
printf("using 'correct_extern_c(11, 4)' output %d\t expected 11\n", correct_extern_c(11, 4));
return 0;
}
And the output is this:
showing results of incorrect and correct use of extern "C" functions
using 'not_extern_c(-1, 4) output 4 expected 4
using 'not_extern_c(11, 4) output 11 expected 11
using 'correct_extern_c(-1, 4) output 4 expected 4
using 'correct_extern_c(11, 4) output 11 expected 11
Like wise, in python:
runlib.py
import ctypes
myLib1 = ctypes.CDLL("../../lib/target/debug/liblib1.dylib")
print("test functions with/without extern 'c'")
print(str.format("using 'not_extern_c(-1, 4)' output {}\t expected 4", myLib1.not_extern_c(-1, 4)));
print(str.format("using 'not_extern_c(11, 4)' output {}\t expected 11", myLib1.not_extern_c(11, 4)));
print(str.format("using 'correct_extern_c(-1, 4)' output {}\t expected 4", myLib1.correct_extern_c(-1, 4)));
print(str.format("using 'correct_extern_c(11, 4)' output {}\t expected 11", myLib1.correct_extern_c(11, 4)));
And the output is the same:
test functions with/without extern 'c'
using 'not_extern_c(-1, 4)' output 4 expected 4
using 'not_extern_c(11, 4)' output 11 expected 11
using 'correct_extern_c(-1, 4)' output 4 expected 4
using 'correct_extern_c(11, 4)' output 11 expected 11
So I am curious: what does extern "C" do underneath and why (and when) do I need it?
Thnx
Matt