safer-ffi-banner

CI guide docs-rs crates-io repository

What is safer_ffi?

safer_ffi is a framework that helps you write foreign function interfaces (FFI) without polluting your Rust code with unsafe { ... } code blocks while making functions far easier to read and maintain.

šŸ“š Read The User Guide šŸ“š

Prerequisites

Minimum Supported Rust Version: 1.66.1

Quickstart

Click to hide

Small self-contained demo

You may try working with the examples/point example embedded in the repo:

bash git clone https://github.com/getditto/safer_ffi && cd safer_ffi (cd examples/point && make)

Otherwise, to start using ::safer_ffi, follow the following steps:

Crate layout

Step 1: Cargo.toml

Edit your Cargo.toml like so:

```toml [package] name = "crate_name" version = "0.1.0" edition = "2021"

[lib] crate-type = [ "staticlib", # Ensure it gets compiled as a (static) C library # "cdylib", # If you want a shared/dynamic C library (advanced) "lib", # For downstream Rust dependents: examples/, tests/ etc. ]

[dependencies]

Use cargo add or cargo search to find the latest values of x.y.z.

For instance:

cargo add safer-ffi

safer-ffi.version = "x.y.z" safer-ffi.features = [] # you may add some later on.

[features]

If you want to generate the headers, use a feature-gate

to opt into doing so:

headers = ["safer-ffi/headers"] ```

Step 2: src/lib.rs

Then, to export a Rust function to FFI, add the #[derive_ReprC] and #[ffi_export] attributes like so:

```rust ,norun use ::saferffi::prelude::*;

/// A struct usable from both Rust and C

[derive_ReprC]

[repr(C)]

[derive(Debug, Clone, Copy)]

pub struct Point { x: f64, y: f64, }

/* Export a Rust function to the C world. */ /// Returns the middle point of [a, b].

[ffi_export]

fn mid_point(a: &Point, b: &Point) -> Point { Point { x: (a.x + b.x) / 2., y: (a.y + b.y) / 2., } }

/// Pretty-prints a point using Rust's formatting logic.

[ffi_export]

fn print_point(point: &Point) { println!("{:?}", point); }

// The following function is only necessary for the header generation.

[cfg(feature = "headers")] // c.f. the Cargo.toml section

pub fn generateheaders() -> ::std::io::Result<()> { ::saferffi::headers::builder() .tofile("rustpoints.h")? .generate() } ```

Step 3: src/bin/generate-headers.rs

rust ,ignore fn main() -> ::std::io::Result<()> { ::crate_name::generate_headers() }

Compilation & header generation

```bash

Compile the C library (in target/{debug,release}/libcrate_name.ext)

cargo build # --release

Generate the C header

cargo run --features headers --bin generate-headers ```

Generated C header (rust_points.h)

``C /*! \file */ /******************************************* * * * File auto-generated by::safer_ffi`. * * * * Do not manually edit this file. * * * *************/

ifndef RUSTCRATENAME

define RUSTCRATENAME

ifdef __cplusplus

extern "C" {

endif

include

include

/* \brief * A struct usable from both Rust and C */ typedef struct Point { /* */ double x;

/** <No documentation available> */
double y;

} Point_t;

/** \brief * Returns the middle point of [a, b]. */ Pointt midpoint ( Pointt const * a, Pointt const * b);

/** \brief * Pretty-prints a point using Rust's formatting logic. */ void printpoint ( Pointt const * point);

ifdef __cplusplus

} /* extern \"C\" */

endif

endif /* RUSTCRATENAME */

```


Testing it from C

Here is a basic example to showcase FFI calling into our exported Rust functions:

main.c

```C

include

include "rust_points.h"

int main (int argc, char const * const argv[]) { Pointt a = { .x = 84, .y = 45 }; Pointt b = { .x = 0, .y = 39 }; Pointt m = midpoint(&a, &b); printpoint(&m); return EXITSUCCESS; } ```

Compilation command

```bash cc -o main{,.c} -L target/debug -l crate_name -l{pthread,dl,m}

Now feel free to run the compiled binary

./main ```

which does output:

text Point { x: 42.0, y: 42.0 }

šŸš€šŸš€

Development

Tests

safer-ffi includes three different tests suites that can be run.

```bash

In the project root:

cargo test

FFI tests

make -C ffi_tests

JavaScript tests

make -C js_tests

Running the JS tests also gives you instructions for running browser tests.

Run this command in the js_tests directory, open a browser and navigate to

http://localhost:13337/

wasm-pack build --target web && python3 -m http.server 13337

```