# FFI in rust
@wusyong, 吳昱緯
----
* Rust ABI is not stable.
* Rust supports the platform-ABI (see [External blocks](https://doc.rust-lang.org/reference/items/external-blocks.html))
* Effecient C bindings
---
## Using C from Rust
----
### printf from C
假設我們真的很想用 printf
----
```c
#include "hello.h"
#include <stdio.h>
uint32_t printf_wrapper(const char *str)
{
printf("%s\n", str);
return 0;
}
```
```c
#include <stdint.h>
uint32_t printf_wrapper(const char *str);
```
----
- Bind functions in header
- Link the library
- Call them in `unsafe`
- Pass parameter to C functions
----
### Disable Rust naming lints
```rust
#![allow(non_camel_case_types)]
#![allow(non_upper_case_globals)]
#![allow(non_snake_case)]
```
----
```c
#include <stdint.h>
uint32_t printf_wrapper(const char *str);
```
```rust
use std::os::raw::{c_uint, c_char};
extern "C" {
printf_wrapper(strr: const *c_char) -> c_uint;
}
```
----
### Primitive types
`std::os::raw` & `std::ffi`
- c_uint ↔ u32
- c_int ↔ i32
- c_void ↔ ()
- CString, Cstr, ...
----
```rust
use std::os::raw::{c_uint, c_char};
use std::ffi::CStr;
extern "C" { fn printf_wrapper(strr: const *c_char) -> c_uint; }
fn main() {
unsafe {
let my_strr = "Hello World!";
let _: u32 = printf_wrapper(CStr::from(my_strr).unwrap());
}
}
```
----
### Structs
`#[repr(C)]` directs the compiler to use the platform-layout.
```rust
#[repr(C)]
pub struct myapp_t {
id: u32,
other_type: Box<myother_t>,
initialised: bool,
}
```
----
### Enum
```rust
#[repr(C)]
pub enum FakeBool {
No = 0,
OhYea = 1
}
```
----
### Opaque type
When not knowing (or caring) about internal layout, opaque structs can be used.
```rust
#[repr(C)]
pub struct myopaque_t { _priv: [u8; 0] }
```
----
### Callbacks
```rust
extern "C" {
pub fn register_callback(
state: *mut c_void,
name: *const c_char,
cb: extern "C" fn(state: *mut c_void) -> *const c_uint,
);
}
```
----
### Cargo (build-system) support
* Build native code via build-dependency crates:
* gcc, clang, cmake, ...
* `build.rs` file responsible for linking code
* More details on [Build Scripts](https://doc.rust-lang.org/cargo/reference/build-scripts.html)
----
```
[build-dependencies]
cmake = "0.1"
```
```rust
use cmake::Config;
fn main() {
let mut dst = Config::new("mimalloc")
.define("MI_OVERRIDE", "OFF")
.define("MI_SECURE", "OFF")
.build_target("mimalloc-static")
.build();
dst.push("./build");
let out_name = "mimalloc";
println!("cargo:rustc-link-search=native={}", dst.display());
println!("cargo:rustc-link-lib=static={}", out_name);
}
```
---
## Real Example
Binding mimalloc to rust!
常用的函式庫通常都有人弄好了
----
![](https://i.imgur.com/Hd8SMQa.png)
----
### [bindgen](https://rust-lang.github.io/rust-bindgen/)
Instead of write all `extern "C"` block yourelf, generate them!
----
```rust
// Continue in build.rs ...
let bindings = bindgen::Builder::default()
.header("mimalloc/include/mimalloc.h")
.generate()
.expect("Unable to generate bindings");
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
bindings
.write_to_file(out_path.join("bindings.rs"))
.expect("Couldn't write bindings!");
```
----
```rust
// In src/lib.rs
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
```
---
### Building Layers
```rust
pub struct MiMalloc;
unsafe impl GlobalAlloc for MiMalloc {
#[inline]
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
if Self::is_aligned(&layout) {
mi_malloc(layout.size()) as *mut u8
} else {
mi_malloc_aligned(layout.size(), layout.align()) as *mut u8
}
}
// ...
}
```
----
### Memory management
Implement `Drop` trait for your instance in most cases
```rust
#[inline]
unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {
mi_free(ptr as *mut _);
}
```
---
## Using Rust from C
Use platform-ABI in reverse – emit a native library
----
### Similar concepts
* extern "C" functions.
* `#[repr(C)]` structures/ enums.
* Making data C compatible.
* Panic is undefine behavior! Use `catch_unwind`
----
```rust
use std::panic::catch_unwind;
#[no_mangle]
pub extern fn hello_rust() -> *const u8 {
"Hello, world!\0".as_ptr()
}
#[no_mangle]
pub extern fn oh_no() -> i32 {
let result = catch_unwind(|| {
panic!("Oops!");
});
match result {
Ok(_) => 0,
Err(_) => 1,
}
}
```
----
### Cargo settings
[Building dynamic or static libraries](https://doc.rust-lang.org/cargo/reference/manifest.html#building-dynamic-or-static-libraries)
```
[lib]
name = "connector"
crate-type = ["cdylib"]
```
----
### Creating bindings
###### (No, this is not a deja-vu)
C code needs .h files to include – define Rust functions there.
----
### [cbindgen](https://github.com/eqrion/cbindgen)
- great tool to auto-generate .h files
- Use as an application with toml configuration
- Or just build with build script
---
## Reference
[The Reference](https://doc.rust-lang.org/reference/items/external-blocks.html) (duh)
[The Rustonomicon](https://doc.rust-lang.org/nomicon/ffi.html)
[Making a `*-sys` crate](https://kornel.ski/rust-sys-crate)
{"metaMigratedAt":"2023-06-15T00:54:39.520Z","metaMigratedFrom":"YAML","title":"FFI","breaks":"true","description":"FFI","contributors":"[{\"id\":\"9913fcec-c953-46f3-8b1c-33170571dfd1\",\"add\":7594,\"del\":2167}]"}