How to wrap existing C functions with Rust or how to call C functions from Rust?
If you're trying to call C code from Rust, you need to create FFI bindings as some people have mentioned in the comments.
However, it is usually best to do this via rust-bindgen
, which automatically generates the bindings and includes tests to make sure that the results are properly sized, aligned, etc. It is very easy to create a type with an incorrect size, which will give you no warning at compile-time and can cause undefined behavior unless you generate tests for them with bindgen
.
How to call a Rust function in C?
If you have this C code compiled to a library named lib.dll
, exporting a single function which accepts a pointer to a function which takes no arguments and returns nothing:
__declspec(dllexport) void foo(void (*callback)()) {
callback();
}
Then this Rust code will send the function callback
to that C function. Upon execution, it prints the line "callback()"
:
extern "C" fn callback() -> () {
println!("callback()");
}
fn main() {
println!("main()");
call_dynamic();
}
fn call_dynamic() -> Result<u32, Box<dyn std::error::Error>> {
unsafe {
let lib = libloading::Library::new("lib.dll")?;
let foo: libloading::Symbol<extern "C" fn(extern "C" fn()) -> u32> = lib.get(b"foo")?;
Ok(foo(callback))
}
}
You should see this in your console:
main()
callback()
I am on Windows and compiled the C code with this command:
gcc --shared lib.c -o lib.dll
The Rust code was run with this:
cargo run
Is it possible to receive a pointer to C function in Rust and call it back?
You can call freely, but unsafely, from C to Rust and from Rust to C.
From Rust to C: write a extern "C"
block with the prototypes of the C functions.
From C to Rust: write the Rust function with a #[no_mangle] extern "C"
prefix. You must ensure that any value received or returned is FFI friendly: basic types or repr(C)
types or raw pointers to those and a few others.
Also you will need the equivalent C declarations to those of Rust. for that you can use the types in std::os::raw
, std::ffi
and the external crate libc
.
Particularly, the answer to your questions about function pointers, a type such as this in C:
typedef int (*callback)(uint8_t *data, size_t len);
is translated into Rust as:
type Callback = unsafe extern "C" fn(data: *mut u8, len: usize) -> c_int;
This type Callback
is FFI friendly, so you can store it, pass between Rust and C, and call it whenever you want. And of course you can use it as argument or return type in other FFI declarations:
int do_the_thing(uint8_t*data, size_t len, callback cb);
#[no_mangle]
pub extern "C" fn do_the_thing(
data: *const u8, len: usize,
cb: Option<Callback>) -> c_int)
{
//...
}
Remember that in Rust pointer functions are non-nullable, so if you want C to be able to pass NULL
you use Option<fn>
.
For more details about FFI in Rust you can read this chapter of the Rustonomicon.
How can I export a function written in Rust to C code?
The problem is coming from the forward declaration from bindgen. Rust unlike C and C++ does not have forward declaration. So remove this:
extern "C" {
pub fn ErrorHandler(Error: StatusType);
}
and keep this:
#[no_mangle]
pub extern "C" fn ErrorHandler(Error: StatusType) {
/* do something */
}
Problem with calling C++ function that receive command line arguments from Rust
The problem is in this code:
let args = std::env::args()
.map(|arg| CString::new(arg).unwrap())
.collect::<Vec<CString>>();
// ...
let c_args_ptr = args.as_ptr() as *const c_char;
That creates a vector of CString
objects, which you then proceed to cast into an array of pointers. But a CString
consists of two word-sized values, a pointer and a length, and cannot be reinterpreted as a single pointer. To get an actual array of pointers which print_args()
expects, you need to collect them into a separate vector:
let args = std::env::args()
.map(|arg| CString::new(arg).unwrap())
.collect::<Vec<CString>>();
let arg_ptrs: Vec<*const c_char> = args.iter().map(|s| s.as_ptr()).collect();
let args_len: c_int = args.len() as c_int;
unsafe { print_args(args_len, arg_ptrs.as_ptr()) };
Note that you'll need to declare print_args
as taking pointer to pointer, as it does in C++ (const char *argv[]
is just sugar for const char **argv
):
#[link(name = "library", kind = "static")]
extern "C" {
pub fn print_args(args: c_int, argsv: *const *const c_char);
}
Is it possible to call a function from static library that built in Rust using C?
Setting up a project
Create a new cargo
library as usual:
$ cargo new my_rust_library --lib
You can then configure cargo
to emit a systems library instead of its regular rust target:
[lib]
crate-type = ["cdylib"] # dynamic lib
# crate-type = ["staticlib"] # static lib
Building a C API
You can mark a function as extern "C"
to tell the compiler to use the C ABI. You also need to add the #[no_mangle]
annotation to tell the compiler not to mangle the name of this function:
#[no_mangle]
pub extern "C" fn message() {
println!("Hello C!")
}
Linking
You can build your crate as usual:
$ cargo build --release
cargo
will create a my_lib.so
or my_lib.a
file in the target/release
directory. This library can simply be linked by your build system. However, calling a Rust function from C requires a header file to declare the function signatures.
Every extern fn
needs to have a corresponding header function.
#[no_mangle]
pub extern "C" fn message() {}
would become:
void message(void);
You can automate this process with cbindgen
, a crate which analyzes your Rust code and generates the corresponding C/C++ headers. You can install it with cargo
:
$ cargo install cbindgen
Create an empty config file:
$ touch cbindgen.toml
And generate the header file:
$ cbindgen --config cbindgen.toml --crate my_rust_library --output my_header.h --lang c
Now you can simply include the header and call you Rust functions from C!
#include "my_header.h"
message()
How do I pass arguments by reference when calling Rust from C++?
#[no_mangle]
// See note below
pub extern "C" fn add_one(x: &mut i32) {
*x += 1;
}
#include <iostream>
#include <cstdint>
extern "C" {
void add_one(int32_t *x);
} // extern "C"
using namespace std;
int main() {
int32_t x = 14;
cout << x << endl;
add_one(&x);
cout << x << endl;
cout << x << endl;
}
By using &mut
in the function argument, we are requiring that the caller provides a valid reference. Among other things, that requires that:
- It not be NULL
- It be properly aligned
- It doesn't alias any other value.
It's up to the caller of the function to ensure these conditions, otherwise it will cause undefined behavior.
See also:
- The Rust FFI Omnibus
- Passing a Rust variable to a C function that expects to be able to modify it
- How do I pass a reference to mutable data in Rust?
- What's the difference between placing "mut" before a variable name and after the ":"?
Related Topics
How to Add Playable(Such as Wav,Wmv) Header with Pcm Data/Buffer in iOS
Documenting Preprocessor Defines in Doxygen
C++: What Is the Printf() Format Spec for "Float"
How to Get the Precision of High_Resolution_Clock
Programmatically Getting Per-Process Disk Io Statistics on Windows
Is It a Strict Aliasing Violation to Alias a Struct as Its First Member
Understanding Y Combinator Through Generic Lambdas
Opencv Cvsaveimage Jpeg Compression Factor
Cocreateinstance Returning E_Nointerface Even Though Interface Is Found
Why Was the Restriction on the Comma Operator Being in a Constant Expression Removed in C++11
How to Control Memory Allocation Strategy in Third Party Library Code
Does the Compiler Optimize the Function Parameters Passed by Value
Building Qt 4.5 with Visual C++ 2010