fn hello(param1: i32, param2: f64) -> SomeStruct { ... }
» efficient C bindings «
(Like an API, but for machine code calling machine code)
Rust ABI is not stable.
Rust also supports the platform-ABI(s).
(Windows has two…)
CPUs have registers, and they have a pointer to the stack (in RAM)
Where does this function find its arguments? Where does the return value go?
fn hello(param1: i32, param2: f64) -> SomeStruct { ... }
Your Rust code might want to interact with shared/static libraries.
Or be one.
» efficient C bindings «
There are no conversion costs
We have this amazing Rust library, we want to use in our existing C project.
struct MagicAdder {
amount: u32
}
impl MagicAdder {
fn new(amount: u32) -> MagicAdder {
MagicAdder {
amount
}
}
fn process_value(&self, value: u32) -> u32 {
self.amount + value
}
}
Tell C these functions exist
Tell Rust to use C-compatible types and functions
Link the external code as a library
Provide some C types that match the Rust types
Call our Rust functions
#[repr(C)]
struct MagicAdder {
amount: u32
}
impl MagicAdder { /* Snip... */ }
#[no_mangle]
extern "C" fn magicadder_new(amount: u32) -> MagicAdder {
MagicAdder::new(amount)
}
#[no_mangle]
extern "C" fn magicadder_process_value(adder: *const MagicAdder, value: u32) -> u32 {
if let Some(ma) = unsafe { adder.as_ref() } {
ma.process_value(value)
} else {
0
}
}
/// Designed to have the exact same shape as the Rust version
typedef struct magic_adder_t {
uint32_t amount;
} magic_adder_t;
/// Wraps MagicAdder::new
magic_adder_t magicadder_new(uint32_t amount);
/// Wraps MagicAdder::process_value
uint32_t magicadder_process_value(magic_adder_t* self, uint32_t value);
You can tell rustc
to make:
binaries (bin)
libraries (lib)
rlib
dylib
staticlib
cdylib
[package]
name = "magic_adder"
version = "1.0.0"
edition = "2021"
[lib]
crate-type = ["lib", "staticlib", "cdylib"]
We have this amazing C library, we want to use as-is in our Rust project.
#include <stdint.h>
/** Do some amazing maths */
uint32_t cool_library_function(uint32_t x, uint32_t y);
#include <stdio.h>
#include "hello.h"
uint32_t cool_library_function(uint32_t x, uint32_t y)
{
// I know, right? Amazing.
return x + y;
}
Tell Rust these functions exist
Link the external code as a library
Call those with unsafe { … }
Transmute data for C functions
#![allow(non_camel_case_types, non_upper_case_globals, non_snake_case)]
Disables some Rust naming lints
#include <stdint.h>
/** Do some amazing maths */
uint32_t cool_library_function(uint32_t x, uint32_t y);
use std::os::raw::{c_uint, c_char};
extern "C" {
// We state that this function exists, but there's no definition.
// The linker looks for this 'symbol name' in the other objects
fn cool_library_function(x: c_uint, y: c_uint) -> c_uint;
}
Some type conversions can be infered by the compiler.
c_uint
↔ u32
c_int
↔ i32
c_char
↔ u8
(not char
!)
c_void
↔ ()
…etc…
use std::os::raw::c_uint;
extern "C" {
fn cool_library_function(x: c_uint, y: c_uint) -> c_uint;
}
fn main() {
let result: u32 = unsafe {
cool_library_function(6, 7)
};
println!("{} should be 13", result);
}
When not knowing (or caring) about internal layout, opaque structs can be used.
/// This is like a 'struct FoobarContext;' in C
#[repr(C)]
pub struct FoobarContext { _priv: [i32; 0] }
extern "C" {
fn foobar_init() -> *mut FoobarContext;
fn foobar_do(ctx: *mut FoobarContext, foo: i32);
fn foobar_destroy(ctx: *mut FoobarContext);
}
/// Use this in your Rust code
pub struct FoobarHandle(*mut FoobarContext);
extern "C"
applies to function pointers given to extern functions too.
use std::os::raw::c_void;
pub type FooCallback = extern "C" fn(state: *mut c_void);
extern "C" {
pub fn libfoo_register_callback(state: *mut c_void, cb: FooCallback);
}
extern "C" fn my_callback(_state: *mut c_void) {
// Do stuff here
}
fn main() {
unsafe { libfoo_register_callback(core::ptr::null_mut(), my_callback); }
}
There’s a better way!
#[allow(non_camel_case_types, non_snake_case, non_upper_case_globals)]
pub mod bindings {
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
}
On the command line
Executing a command in build.rs
Calling a library function in build.rs
xxxx-sys
is a Rust crate that provides a thin wrapper around some C library xxxx
.
You normally have a higher-level xxxx
crate that provides a Rust interface