• Stars
    star
    903
  • Rank 50,580 (Top 1.0 %)
  • Language
    Rust
  • License
    MIT License
  • Created over 4 years ago
  • Updated 3 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Write safer FFI code in Rust without polluting it with unsafe code

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:

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:

[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"]
  • Where "x.y.z" ought to be replaced by the last released version, which you can find by running cargo search safer-ffi.

  • See the dedicated chapter on Cargo.toml for more info.

Step 2: src/lib.rs

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

use ::safer_ffi::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 generate_headers() -> ::std::io::Result<()> {
    ::safer_ffi::headers::builder()
        .to_file("rust_points.h")?
        .generate()
}

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

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

Compilation & header generation

# 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)
/*! \file */
/*******************************************
 *                                         *
 *  File auto-generated by `::safer_ffi`.  *
 *                                         *
 *  Do not manually edit this file.        *
 *                                         *
 *******************************************/

#ifndef __RUST_CRATE_NAME__
#define __RUST_CRATE_NAME__
#ifdef __cplusplus
extern "C" {
#endif


#include <stddef.h>
#include <stdint.h>

/** \brief
 *  A `struct` usable from both Rust and C
 */
typedef struct Point {
    /** <No documentation available> */
    double x;

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

/** \brief
 *  Returns the middle point of `[a, b]`.
 */
Point_t
mid_point (
    Point_t const * a,
    Point_t const * b);

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


#ifdef __cplusplus
} /* extern \"C\" */
#endif

#endif /* __RUST_CRATE_NAME__ */

Testing it from C

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

main.c

#include <stdlib.h>

#include "rust_points.h"

int
main (int argc, char const * const argv[])
{
    Point_t a = { .x = 84, .y = 45 };
    Point_t b = { .x = 0, .y = 39 };
    Point_t m = mid_point(&a, &b);
    print_point(&m);
    return EXIT_SUCCESS;
}

Compilation command

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

# Now feel free to run the compiled binary
./main
  • Note regarding the extra -l… flags.

    Those vary based on the version of the Rust standard library being used, and the system being used to compile it. In order to reliably know which ones to use, rustc itself ought to be queried for it.

    Simple command:

    rustc --crate-type=staticlib --print=native-static-libs -</dev/null

    this yields, to the stderr, output along the lines of:

    note: Link against the following native artifacts when linking against this static library. The order and any duplication can be significant on some platforms.
    
    note: native-static-libs: -lSystem -lresolv -lc -lm -liconv
    

    Using something like sed -nE 's/^note: native-static-libs: (.*)/\1/p' is thus a convenient way to extract these flags:

    rustc --crate-type=staticlib --print=native-static-libs -</dev/null \
        2>&1 | sed -nE 's/^note: native-static-libs: (.*)/\1/p'

    Ideally, you would not query for this information in a vacuum (e.g., /dev/null file being used as input Rust code just above), and rather, would apply it for your actual code being compiled:

    cargo rustc -q -- --print=native-static-libs \
        2>&1 | sed -nE 's/^note: native-static-libs: (.*)/\1/p'

    And if you really wanted to polish things further, you could use the JSON-formatted compiler output (this, for instance, avoids having to redirect stderr). But then you'd have to use a JSON parser, such as jq:

    RUST_STDLIB_DEPS=$(set -eo pipefail && \
        cargo rustc \
            --message-format=json \
            -- --print=native-static-libs \
        | jq -r '
            select (.reason == "compiler-message")
            | .message.message
        ' | sed -nE 's/^native-static-libs: (.*)/\1/p' \
    )

    and then use:

    cc -o main{,.c} -L target/debug -l crate_name ${RUST_STDLIB_DEPS}

which does output:

Point { x: 42.0, y: 42.0 }

πŸš€πŸš€

Development

Tests

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

# 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

More Repositories

1

rust-bitcode

Rust toolchain for Xcode-compatible iOS bitcode
Shell
408
star
2

docs

Ditto Documentation
TypeScript
34
star
3

react-ditto

React wrappers for Ditto for easy integration using both components or hooks
TypeScript
29
star
4

bluetooth-blog-tutorial

A tutorial using Core Bluetooth and Android Bluetooth from Scratch
Swift
18
star
5

samples

Sample Codes using Ditto
Kotlin
14
star
6

wifi-aware-checker

A test android app for version 8.0 or higher that checks if WiFi Aware is available on the device
Kotlin
12
star
7

moodlight

Demo project to sync color changes to an Raspberry Pi Mood Light via iOS/Android
Swift
11
star
8

DittoSwiftPackage

Swift package for the DittoSwift framework.
Swift
10
star
9

DittoSwiftTools

Diagnostic and Debugging Tools for DittoSwift
Swift
10
star
10

demoapp-chat

Chat demo for Android and iOS
Kotlin
9
star
11

react-in-mobile

Bundle and run a React web app inside of iOS and Android
Kotlin
9
star
12

rn-jsi-rust-bridging

Sample app for the Bridging React Native and Rust via JSI blog post.
Ruby
8
star
13

demoapp-kmp

Example of using the Ditto iOS & Android SDKs in a Kotlin Multiplatform app.
Kotlin
6
star
14

mobile-microservices

Running HTTP Servers on iOS and Android Devices
Swift
6
star
15

DittoAndroidTools

Diagnostic and Debugging Tools for Ditto Android SDK
Kotlin
6
star
16

cross-platform-rust-js-boilerplate

A boilerplate for cross platform js repo for web browsers, node and react native with a rust code
Rust
5
star
17

starfish

An experimental react native implementation for Ditto using OnlinePlayground
TypeScript
5
star
18

CombineDitto

DittoSwift extension methods for Apple's Combine Framework
Swift
5
star
19

vaxx

Swift
4
star
20

demoapp-flutter

Dart
4
star
21

authtool

Generate authentication for devices in a Ditto mesh
Rust
3
star
22

sample-authentication-permissions

Ditto permissions examples
Swift
3
star
23

dijkstra-calculator

A simple JS calculator for finding the shortest path in a graph of nodes.
TypeScript
3
star
24

react-native-sample-app

A React Native app that lets you create tasks and sync them with Ditto.
TypeScript
3
star
25

demoapp-pos-kds

Swift
3
star
26

DittoObjC

Swift package for the DittoObjC framework.
Swift
3
star
27

template-heartbeat

Monitor the full health of ditto end-to-end
TypeScript
2
star
28

sample-cdc

Ditto Big Peer Syncing with an External Database
TypeScript
2
star
29

rxjs-ditto

RxJS Wrapper for Ditto
TypeScript
2
star
30

CoreDataDitto

Experimental Sync with Core Data
Swift
2
star
31

demoapp-skyservice

SkyService Demo App (iOS and Android)
Swift
2
star
32

template-app-jetpack-compose-kotlin-todo

Kotlin
2
star
33

template-app-nodejs-todo

TypeScript
2
star
34

example-swift-bridge-to-objectivec

[Public] Example iOS app that shows how to use DittoSwift codes in an Objective-C app.
Objective-C
2
star
35

network-graph-addon

A blender addon to make curves between selected objects
Python
1
star
36

social-proximity

Swift
1
star
37

CrashReporterFreeze

Demonstrates freeze issues with PLCrashReporter and Ditto build with Rust 1.65+
Objective-C
1
star
38

DittoDotnetTools

C#
1
star
39

cod-polaris-m1

Common Operational Database services and prototypes.
TypeScript
1
star
40

RxSwiftDitto

RxSwift extension methods for DittoSwift
Swift
1
star
41

dex-k8s-authenticator

A Kubernetes Dex Client Authenticator
Go
1
star
42

rxjavaditto

RxJava 3 extension methods for Kotlin with a Jetpack Compose Example
Kotlin
1
star
43

DittoChatPackage

A swift package of a chat feature
Swift
1
star
44

template-app-swift-todo

Swift
1
star
45

DittoSwiftLog

An example of how to connect DittoLogger's custom callback to an external logging system. This example connects it to Apple's swift-log.
Swift
1
star
46

template-rust

A sample app showing how to sync offline with Ditto using Rust
Rust
1
star
47

convert-html-to-pug

A Visual Studio Code extension that allows you to right click selected HTML and convert it to Pug
TypeScript
1
star
48

moodlight-Bus

Demo project to sync color changes over the Ditto Bus on IOS/Android
Swift
1
star